Flutter how to remove button after change route - flutter

I'm stuck on this case. I have routing on the flutter app where is a gradient background. When a route is changed animations are run but the content of the previous page is still visible. How can I remove the content of the previous page?
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: <Color>[Colors.redAccent, Colors.blueAccent],
),
),
child: const Screen1(),
),
);
}
}
class Screen1 extends StatelessWidget {
const Screen1({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: CupertinoButton(
color: Colors.black12,
child: const Text('Click'),
onPressed: () {
Navigator.push(
context,
_createRoute(this, const Screen2()),
);
},
),
);
}
}
class Screen2 extends StatelessWidget {
const Screen2({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.transparent,
body: Center(
child: CupertinoButton(
color: Colors.black12,
child: const Text('Back'),
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
}
Route _createRoute(Widget exitPage, Widget enterPage) {
return PageRouteBuilder(
opaque: false,
pageBuilder: (context, animation, secondaryAnimation) => enterPage,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
const begin = Offset(1, 0);
const zero = Offset.zero;
const curve = Curves.easeInOut;
var tween = Tween<Offset>(begin: begin, end: zero)
.chain(CurveTween(curve: curve));
var tween2 = Tween<Offset>(begin: zero, end: const Offset(-1, 0))
.chain(CurveTween(curve: curve));
return Stack(
children: <Widget>[
SlideTransition(
position: tween.animate(animation),
child: enterPage,
),
SlideTransition(
position: tween2.animate(animation),
child: exitPage,
),
],
);
},
);
}
Link to dartpad: https://dartpad.dev/?id=c69d10993772b2bbe76c729063866dc3
Thanks for your reply :)

here is your solution,
first solution : simply change PageRouteBuilder opaque value to true.
or
if you want those gradient for all your routing pages,
use this,
class gradientbg extends StatelessWidget {
Widget child;
gradientbg({#required this.child});
#override
Widget build(BuildContext context) {
return Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: <Color>[Colors.redAccent, Colors.blueAccent],
),
),
child: child,
);
}
}
you can use like this,
#override
Widget build(BuildContext context) {
return gradientbg(
child: Scaffold(
backgroundColor: Colors.transparent,
)
);

Related

Flutter UncontrolledProviderScope error with Riverpod

Here i have a simple animated dialog which with that i can show one screen into that. this screen into animated dialog sending request on appearing into dialog and can be receive data from server without any problem. but when i drag this dialog on top of screen to get full width and height i get this error:
This UncontrolledProviderScope widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
in fact i think this screen send multiple request when appear into dialog and when its shows as a new screen instance
problem is
ref.read(profileProvider.notifier).send(
method: HTTP.POST,
endPoint: Server.$userProfile,
parameters: {
'uuid': '123',
},
);
into MyProfile class
Full source code:
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:my_app/core/service/server.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'core/service/repository/request_repository.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(
ProviderScope(child: MyApp()),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'test',
home: MyHome(),
);
}
}
class MyHome extends HookConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
GestureDetector(
onLongPress: () {
showGeneralDialog(
context: context,
barrierDismissible: true,
barrierLabel: "hi",
// barrierColor: Colors.black.withOpacity(0.2),
// transitionDuration: Duration(milliseconds: 500),
pageBuilder: (context, pAnim, sAnim) {
return const SafeArea(
child: FloatingDialog(
previewType: PreviewDialogsType.profile,
));
},
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,
);
}
},
);
},
child: Container(
width: double.infinity,
height: 50.0,
color: Colors.indigoAccent,
child: const Text('LongPress please'),
),
),
],
),
);
}
}
enum PreviewDialogsType {
profile,
}
class FloatingDialog extends StatefulWidget {
final PreviewDialogsType previewType;
const FloatingDialog({super.key, required this.previewType});
#override
_FloatingDialogState createState() => _FloatingDialogState();
}
class _FloatingDialogState extends State<FloatingDialog> with TickerProviderStateMixin {
late double _dragStartYPosition;
late double _dialogYOffset;
late AnimationController _returnBackController;
late Animation<double> _dialogAnimation;
#override
void initState() {
super.initState();
_dialogYOffset = 0.0;
_returnBackController = AnimationController(vsync: this, duration: const Duration(milliseconds: 1300))
..addListener(() {
setState(() {
_dialogYOffset = _dialogAnimation.value;
});
});
}
#override
void dispose() {
_returnBackController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
Widget myContents = PreviewDialog(
type: widget.previewType,
);
return Padding(
padding: const EdgeInsets.only(
top: 100.0,
bottom: 10.0,
left: 16.0,
right: 16.0,
),
child: Transform.translate(
offset: Offset(0.0, _dialogYOffset),
child: Column(
children: <Widget>[
const Icon(
Icons.keyboard_arrow_up,
color: Colors.white,
size: 30.0,
),
Expanded(
child: GestureDetector(
onVerticalDragStart: (dragStartDetails) {
_dragStartYPosition = dragStartDetails.globalPosition.dy;
},
onVerticalDragUpdate: (dragUpdateDetails) {
setState(() {
_dialogYOffset = (dragUpdateDetails.globalPosition.dy) - _dragStartYPosition;
});
if (_dialogYOffset < -130) {
Navigator.of(context).pop();
Navigator.of(context).push(
PageRouteBuilder(
pageBuilder: (context, pAnim, sAnim) => myContents,
transitionDuration: const 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 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 PreviewDialog extends StatefulWidget {
final PreviewDialogsType type;
final String? uuid;
const PreviewDialog({Key? key, required this.type, this.uuid}) : super(key: key);
#override
State<PreviewDialog> createState() => _PreviewDialogState();
}
class _PreviewDialogState extends State<PreviewDialog> {
late ScrollController scrollController;
#override
void initState() {
super.initState();
scrollController = ScrollController();
}
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: Scaffold(
body: MyProfile(),
),
),
);
}
}
class MyProfile extends HookConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
useEffect(() {
ref.read(profileProvider.notifier).send(
method: HTTP.POST,
endPoint: Server.$userProfile,
parameters: {
'uuid': '123',
},
);
return () {};
}, []);
return SafeArea(
child: Scaffold(
body: Container(
width: double.infinity,
height:double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ref.watch(profileProvider).when(
idle: () => const SizedBox(),
loading: () => const CircularProgressIndicator(),
success: (success) {
return const Text('successful');
},
error: (error, stackTrace) {
return Text('error');
},
)
],
),
),
),
);
}
}

AnimationBuilder with FutureBuilder - How to execute animation after future returns and builds?

I am making an api call to get some data and then I want to animate in (from the bottom) the widget that displays it. The code for each piece works separately but I can't seem to figure out how to execute the animation after the FutureBuilder has returned from its builder function? Where or how do I call cardController.forward()?
Widget mapCard(Area area) {
return Align(
alignment: Alignment.bottomCenter,
child: AnimatedBuilder(
builder: (BuildContext context, Widget child) {
return InkWell(
onTap: () {
Navigator.of(context).push(_createHeroRoute(area));
},
child: Container(
margin: cardMargin.value,
width: 300,
height: 150,
child: Hero(
tag: 'location',
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: 274,
height: 150,
child: Image.network(area.images[0].url,
fit: BoxFit.cover)),
Align(
alignment: Alignment.centerRight,
child: Container(
height: 150,
//30, 42, 65
// 412A1E
decoration: new BoxDecoration(
color: Color.fromARGB(255, 30, 42, 65),
),
child:
Icon(Icons.arrow_right, color: Colors.white))),
],
),
),
),
);
},
animation: cardController,
),
);
}
In the build stack:
FutureBuilder(
future: fArea,
builder: (context, AsyncSnapshot<Area> snap) {
if (snap.hasData) {
return mapCard(snap.data);
} else {
return Text('here');
}
})
You can copy paste run full code below
You can make MapCard a StatefulWidget and call cardController.forward in initState
#override
void initState() {
super.initState();
cardController =
AnimationController(duration: const Duration(seconds: 10), vsync: this);
animation = Tween<double>(begin: 0, end: -300.0).animate(cardController)
..addStatusListener((state) => print('$state'));
cardController.forward();
}
working demo
full code
import 'package:flutter/material.dart';
import 'dart:math' as math;
class MapCard extends StatefulWidget {
Area area;
MapCard(Area data, {Key key}) : super(key: key);
#override
_MapCardState createState() => _MapCardState();
}
class _MapCardState extends State<MapCard> with TickerProviderStateMixin {
AnimationController cardController;
Animation<double> animation;
#override
void initState() {
super.initState();
cardController =
AnimationController(duration: const Duration(seconds: 10), vsync: this);
animation = Tween<double>(begin: 0, end: -300.0).animate(cardController)
..addStatusListener((state) => print('$state'));
cardController.forward();
}
#override
void dispose() {
cardController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Align(
alignment: AlignmentDirectional.bottomCenter,
child: AnimatedBuilder(
animation: cardController,
child: InkWell(
onTap: () {
//Navigator.of(context).push(_createHeroRoute(area));
},
child: Container(
//margin: cardMargin.value,
width: 300,
height: 150,
child: Hero(
tag: 'location',
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: 274,
height: 150,
child: Image.network("https://picsum.photos/250?image=9",
fit: BoxFit.cover)),
Align(
alignment: Alignment.centerRight,
child: Container(
height: 150,
//30, 42, 65
// 412A1E
decoration: new BoxDecoration(
color: Color.fromARGB(255, 30, 42, 65),
),
child: Icon(Icons.arrow_right, color: Colors.white))),
],
),
),
),
),
builder: (BuildContext context, Widget child) {
return Transform.translate(
offset: Offset(0, animation.value),
child: child,
);
},
),
);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
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 Area {
String data;
Area(this.data);
}
class _MyHomePageState extends State<MyHomePage> {
Future<Area> fArea;
Future<Area> getArea() async {
await Future.delayed(Duration(seconds: 3), () {});
return Future.value(Area("data"));
}
#override
void initState() {
fArea = getArea();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: FutureBuilder(
future: fArea,
builder: (context, AsyncSnapshot<Area> snap) {
switch (snap.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snap.hasData) {
return MapCard(snap.data);
} else {
return Center(child: CircularProgressIndicator());
}
}
}));
}
}

How can I create a page that can be slided from the bottom of the screen in Flutter?

How can I create a page that slides from the bottom of the screen and when it passes a certain threshold automatically fills the screen?
Something like this:
Consider using DraggableScrollableSheet class or sliding up panel package
You can use PageRouteBuilder to build your own navigator transitions.
This is the CustomPageRouteBuilder class as you need, and you can change it into other transitons like scale fade rotate, etc.
class CustomPageRouteBuilder<T> extends PageRouteBuilder<T> {
CustomPageRouteBuilder({
this.widget,
})
: assert(widget != null),
super(
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return widget;
},
transitionsBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
final Widget transition = SlideTransition(
position: Tween<Offset>(
begin: Offset(0.0, 1.0),
end: Offset.zero,
).animate(animation),
child: SlideTransition(
position: Tween<Offset>(
begin: Offset.zero,
end: Offset(0.0, -0.7),
).animate(secondaryAnimation),
child: child,
),
);
return transition;
},
);
final Widget widget;
}
And how to use:
Navigator.push(
context,
CustomPageRouteBuilder(
widget: SecondPage(),
),
);
You can check the sample below:
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: FirstPage(),
);
}
}
class FirstPage extends StatelessWidget {
const FirstPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.red,
child: Center(
child: Text(
'First Page',
style: TextStyle(
color: Colors.white,
fontSize: 24.0,
),
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(
Icons.arrow_forward_ios,
color: Colors.white,
),
onPressed: () {
Navigator.push(
context,
CustomPageRouteBuilder(
widget: SecondPage(),
),
);
},
),
);
}
}
class SecondPage extends StatelessWidget {
const SecondPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.blue,
child: Center(
child: Text(
'Second Page',
style: TextStyle(
color: Colors.white,
fontSize: 24.0,
),
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
onPressed: () {
Navigator.pop(context);
},
),
);
}
}
class CustomPageRouteBuilder<T> extends PageRouteBuilder<T> {
CustomPageRouteBuilder({
this.widget,
})
: assert(widget != null),
super(
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return widget;
},
transitionsBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
final Widget transition = SlideTransition(
position: Tween<Offset>(
begin: Offset(0.0, 1.0),
end: Offset.zero,
).animate(animation),
child: SlideTransition(
position: Tween<Offset>(
begin: Offset.zero,
end: Offset(0.0, -0.7),
).animate(secondaryAnimation),
child: child,
),
);
return transition;
},
);
final Widget widget;
}
I would recommend the PageView widget, you can see more here and view the documentation.
Using the scrollDirection property, you can set it to vertical so that you can swipe up to reveal a new page and you can swipe down to go back.

Flutter adding more options for dialogs

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),
),
),
),
),
);
}
}

flutter, PageRouteBuilder, adding Horizontal Transition

The native navigation transition for iOS is RightToLeft. The Native Navigation transition for ANDROID is Bottom to Top. I would like to override the Native Navigation transition in Flutter to have the same RIght to Left transtion across iOS and ANDROID. To do this, I am trying to use PageRouteBuilder but no luck getting it to work. The first code block, I have a very basic screen that works... but natively. The second snippet includes the navigation transition code I am attempting to integrate.
The Code I am trying to fix.
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
//==============================================
// How would I force a horizontal transition?
mbNav001(context),
new Text(
'Screen 1',
),
],
),
),
);
}
}
//===================================================
Padding mbNav001(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(28.0),
child: new MaterialButton(
height: 80.0,
padding: EdgeInsets.all(50.0),
minWidth: double.infinity,
color: Theme.of(context).primaryColor,
textColor: Colors.white,
child: new Text(
"material button a",
style: new TextStyle(
fontSize: 20.0,
color: Colors.yellow,
),
),
splashColor: Colors.redAccent,
// ----- This line is giving me error...
onPressed: () {
print('click mb');
//===========================================================
// How to force a horizontal trans in Navigation?
//===========================================================
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
}
// expecting to find... :
),
);
}
//===================================================
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Screen"),
),
body: Center(
child: RaisedButton(
onPressed: () {
// Navigate back to first screen when tapped!
Navigator.pop(context);
},
child: Text('Go back!'),
),
),
);
}
}
this is the transition code I am trying to add.
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
return SlideTransition(
position: new Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: new SlideTransition(
position: new Tween<Offset>(
begin: Offset.zero,
end: const Offset(1.0, 0.0),
).animate(secondaryAnimation),
child: child,
),
);
},
);
Navigator.of(context).push(pageRoute);
Using CupertinoPageRoute instead of MaterialPageRoute resolved the same issue in my case, without trying to apply any custom animation.
Navigator.of(context).push(CupertinoPageRoute(builder: (context) => MyPanel()));
This is the code i've used to achieve the slide up from the bottom animation on iOS. You'd just need to edit the tween values to achieve a left to right animation.
var builder = PageRouteBuilder(
pageBuilder: (BuildContext context,
Animation animation,
Animation secondaryAnimation) {
return YourChildWidgetHere();
},
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return new SlideTransition(
position: new Tween<Offset>(
begin: const Offset(0.0, 1.0),
end: Offset.zero,
).animate(animation),
child: child,
);
});
Navigator.of(context).push(builder);