flutter, PageRouteBuilder, adding Horizontal Transition - flutter

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

Related

Animating navigation only to and from specific pages in Flutter

Is there a way to set an animation between two specific pages? I'm using onGenerateRoute to change animations between pages, but it can only filter through the page it's being navigated to.
Example: I have PageA, PageB and PageC, i can change the navigation animation when i'm navigating to PageC, but it doesn't matter if i'm navigating from PageA or PageB, the animation will always be the same.
What i'm asking for is if there is a way to make a custom animation specifically when i'm navigating between PageA and PageC and PageB and PageC.
Create Animated Navigation like this:
import 'package:flutter/material.dart';
void main() {
runApp(
const MaterialApp(
home: Page1(),
),
);
}
class Page1 extends StatelessWidget {
const Page1({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).push(_createRoute());
},
child: const Text('Go!'),
),
),
);
}
}
Route _createRoute() {
return PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => const Page2(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
const begin = Offset(0.0, 1.0);
const end = Offset.zero;
const curve = Curves.ease;
var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
return SlideTransition(
position: animation.drive(tween),
child: child,
);
},
);
}
class Page2 extends StatelessWidget {
const Page2({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: const Center(
child: Text('Page 2'),
),
);
}
}

Flutter how to remove button after change route

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

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.

How to make AnimatedSwitcher animate if parent widget is rebuilt in Flutter?

I am trying to implement a selectable ListTile. When ListTile is selected it's leading icon changes and its background color changes. I am trying to use AnimatedSwitcher to animate the transition when icons are changed. And it works (as long as I don't change background color of the list tile).
Changing the background color of the ListTile causes the animation to not work anymore. And I think I know why that is. When background color of the ListTile is changed, entire ListTile gets rebuilt. Which causes AnimatedSwitcher to also be rebuilt instead of transitioning between icons. Is there a way I can implement what I am trying to do. Here is my full code.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
// This widget is the root of your application.
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool change = false;
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Center(
child: SomeStatelessWidget(
AnimatedSwitcher(
duration: Duration(seconds: 2),
child: change
? CircleAvatar(
key: UniqueKey(),
child: Icon(Icons.check),
)
: CircleAvatar(
key: UniqueKey(),
child: Text("A"),
),
transitionBuilder: (child, animation) {
return RotationTransition(
turns: animation,
child: child,
);
},
),
change),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.swap_horiz),
onPressed: () {
setState(() {
change = !change;
});
},
),
floatingActionButtonLocation:
FloatingActionButtonLocation.centerFloat,
));
}
}
class SomeStatelessWidget extends StatelessWidget {
final Widget child;
final bool changed;
SomeStatelessWidget(this.child, this.changed);
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(8.0),
decoration: changed
? BoxDecoration(
color: Theme.of(context).selectedRowColor,
borderRadius: BorderRadius.all(Radius.circular(8.0)),
shape: BoxShape.rectangle)
: null,
child: ListTile(
leading: child,
title: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("This is an example"),
),
),
);
}
}

Flutter Navigator, Horizontal Transiation with PageRoutebuilder

I'm trying to force a horizontal Transiation in the Flutter Navigator Widget. Navigator uses the platform Default Transition from one screen to the next. In iOS, the transition is Right to left. Pops Left to Right. In Android it is Bottom to Top. I believe solution is to use a PageRouteBuilder, but no luck getting it to work. I marked the method with the Navigator Widget that I'd like to modify with a PageRouteBuilder to get desired Horizontal transition.
Code Snippet 2, is the Transition code I have been trying to make work with no luck.
This code works as default transition.
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'),
initialRoute: "/",
routes: {
'/Second': (context) => SecondScreen(),
},
);
}
}
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>[
ButtonMaterial02(),
new Text('NAV DEMO...',),
new Text('How do I get a Horizontal Transition in Android?',),
],
),
),
// This trailing comma makes auto-formatting nicer for build methods.
);
}
//================================================================
//================================================================
Padding ButtonMaterial02() {
return Padding(
padding: const EdgeInsets.all(18.0),
child: new MaterialButton(
onPressed: MatButton02_onPress,
child: new Text("Material Button 02"),
padding: EdgeInsets.all(50.0),
minWidth: double.infinity,
color: Theme.of(context).primaryColor,
),
);
}
// add Horizontal code here
void MatButton02_onPress() {
print(" MaterialButton02 onPressed...");
Navigator.pushNamed(context, '/Second');
//*************************************************
//*************************************************
// HOW do I replace the Navigator above to use
// PageRouteBuilder so I can force ANDROID to
// Transition Right to Left instead of BottomToTop?
//*************************************************
//*************************************************
}
}
//================================================================
//================================================================
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("Second Screen"),
),
body: Center(
child: RaisedButton(
child: new Text("RETURN"),
onPressed: (){
Navigator.pop(context);
},
),
),
);
}
}
//================================================================
//================================================================
Code Snippet 2... transition code I have been trying to use.
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('Second');
You're interested in using the CupertinoPageRoute.
It animates from right to left, designed to imitate iOS's transition animation.
Following the example here, replace MaterialPageRoute references with CupertinoPageRoute and you'll be set!