Hero widget gives animation to different widgets on its own by analyzing. So can I wrap different widgets with Hero so that they get animated and look good? Because am don't know about the outcome or what animation will it show or totally don't show, which widget will and which one not...So can I wrap most of them with her widget? If yes, then which widgets can be wrapped to get animations, and do I have to do any extra work after wrapping with the hero? an example, please.
Or is there any way to apply animations to the whole app in all possible widgets at once, which would be more efficient and tame saving?
Hero widget will automatically create a Hero transition between two navigator routes.
Flutter will figure out where the widget is in both routes and animate the change between locations.
Let's say you want to use a picture as your Hero.
Put it on both the page routes, the one that you'll be transitioning from and the one you'll
be transitioning to.
Then, wrap the image with a Hero widget on both pages. Add a tag. This can be any object. The important thing is to use the same tag on both pages so that Flutter knows which Hero you are animating.
And you're done.
import 'package:flutter/material.dart';
void main() => runApp(HeroApp());
class HeroApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Transition Demo',
home: MainScreen(),
);
}
}
class MainScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Main Screen'),
),
body: GestureDetector(
child: Hero(
tag: 'imageHero',
child: Image.network(
'https://picsum.photos/250?image=9',
),
),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return DetailScreen();
}));
},
),
);
}
}
class DetailScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
child: Center(
child: Hero(
tag: 'imageHero',
child: Image.network(
'https://picsum.photos/250?image=9',
),
),
),
onTap: () {
Navigator.pop(context);
},
),
);
}
}
Related
I have an application (simplified) that looks like this:
working dartpad example:
https://dartpad.dev/cc4e524315e104c272cd06aa7037aa10
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: MyWidget(),
),
bottomNavigationBar: BottomWidget(),
),
);
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text('Hello, World!');
}
}
class BottomWidget extends StatelessWidget {
Widget build(BuildContext context) {
return BottomAppBar(
child: InkWell (
child: Container(
height:100,
color: Colors.pink,
child: Center(
child: Text('this is a bottom navigation bar')
),
),
onTap: () {
showModalBottomSheet(
context: context,
builder: (context) => Container(),
);
}
),
);
}
}
My question is, I want to use showModalBottomSheet to stop user from interacting with the screen, but I need bottomNavigationBar to be shown like this:
Right now it just covers everything including the bottomNavigationBar:
If I change it to showBottomSheet, it works perfectly like the first picture.
Why is their behaviour different?
How can I achieve the result that I want using showModalBottomSheet?
showModalBottomSheet show bottom sheet on top of all widget and layers, if you want to show it under bottomNavigationBar you should use Stack in parent and use a single child into that. its not easy to take a sample and that take a long time to reproduce pseudo code
In flutter, you can for example have two pages with their own sets of widgets that when you navigate from one page to the other, the widgets on the screen will change to the other one.
But is it possible to have a completely seperate layer of widgets, that is drawn on top of all pages and it's widgets would remain when the navigation happens?
Completely possible!
Method 1: Subnavigators
Make the widget you want to stay consistent be at the same level or higher up in the widget tree than the navigator. For example (MaterialApp widget creates navigator automatically at its level)
import 'package:flutter/material.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: Column(
children: [
Container(
height: 100,
color: Colors.red,
),
Expanded(
child: MaterialApp(
home: PageOne(),
),
),
],
));
}
}
class PageTwo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(),
appBar: AppBar(
title: Text("Page Two"),
),
);
}
}
class PageOne extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
child: Text("Open Page Two"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PageTwo()),
);
},
),
),
appBar: AppBar(
title: Text("Page One"),
),
);
}
}
This will keep a red container over any navigation. You could use a Stack instead of a Column if you wanted an overlay. At this point we have two navigators because we have two MaterialApps. Calling navigator.of(context) retrieves the closest one in the widget tree, which will be the subnavigator, allowing our navigation to only affect everything under the second MaterialApp widget
Method 2: Keys
I won't go too in depth on these here. But keys are a way to identify widget across rebuilds. You can give the widget you want to stay consistent a key, and it will be considered the same widget on rebuilds. Check out more information on keys in flutter: https://medium.com/flutter/keys-what-are-they-good-for-13cb51742e7d
I have a Home page for an ecommerce website and I am showing every product in image format on click of every product(image) I want that image to be filled up in next screen which is of info screen of product that also contains the product (image) and some other related stuff. I want to build info page only a single time and just change the product image and other stuff accordingly as per the image (product) being clicked. How can I achieve this I am new to flutter and dont know this logic i came across valuelistenablebuilder but not getting how can i achieve this, Please Help.
Thank you,
By default, Flutter is providing this support by using the Hero widget. Find the code snippets below.
import 'package:flutter/material.dart';
void main() => runApp(HeroApp());
class HeroApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Transition Demo',
home: MainScreen(),
);
}
}
class MainScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Main Screen'),
),
body: GestureDetector(
child: Hero(
tag: 'imageHero',
child: Image.network(
'https://picsum.photos/250?image=9',
),
),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return DetailScreen();
}));
},
),
);
}
}
class DetailScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
child: Center(
child: Hero(
tag: 'imageHero',
child: Image.network(
'https://picsum.photos/250?image=9',
),
),
),
onTap: () {
Navigator.pop(context);
},
),
);
}
}
Make sure the Hero.tag property should be maintained as same for both pages.
Refer the link for more details
I want to place an Iconbutton in the top right corner of my Scaffold that programmatically opens a drawer. It should be the top right corner for every displaytype.
Using an appbar would ruin the look of the page since I just need a small icon that shows that a drawer is available. How do I do this the best way?
My Scaffold is a default one.
How can I achieve this the best way?
You can achieve this using a Stack .
Wrap the body of your Scaffold with a Stack widget and use the Positioned widget as the first child of the stack.
GlobalKey<ScaffoldState> _scafKey = GlobalKey();
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
key: _scafKey,
drawer: YourDrawerWidget(),
body: Stack(
children: <Widget>[
Positioned(
top: 0,
right: 0,
child: IconButton(
icon: Icon(Icons.short_text),
onPressed: (){
_scafKey.currentState.openDrawer();
})),
Container(),
],
),
),
);
}
Replace the container with your widget(the original body of the scaffold).
And also the icon of the IconButton to your Icon.
Here the MyHomePage class is the AppBar you need for your Scaffold. All you need to do is change the icon.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: MyHomePage(),
drawer: Container(
width: 100,
color: Colors.red,
),
),
);
}
}
class MyHomePage extends StatelessWidget implements PreferredSizeWidget {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Container(
color: Colors.transparent,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
AppBarAction(),
],
),
),
);
}
#override
Size get preferredSize => Size.fromHeight(60);
}
class AppBarAction extends StatelessWidget {
#override
Widget build(BuildContext context) {
return IconButton(
icon: Icon(Icons.print),
onPressed: () {
Scaffold.of(context).openDrawer();
},
);
}
}
I'd like to fix an Image in the footer of the screen, so in different phones it stays always in the bottom .
What I tried
I tried to use Column, and then in the mainAxisAlignment I would use MainAxisAlignment.end
Additional Question : Is there a way to create a canvas and place it on the image, so I can use relative coordinate on the image if I want to draw something instead of the full screen
You can use the bottomSheet in the scaffold. This automatically allows you to have the widget fixed at the bottom of the display
return Scaffold(
bottomSheet: yourWidget(),
body: Container(color: Colors.red,)
);
Please write a question for each question you have in the future - that makes them more searchable and useful in the future for other people.
However, I think I can answer both pretty simply so I will.
Firstly, if you want an image to always be at the foot of the screen, you can simply use a the bottomNavigationBar property of the Scaffold. It looks like it would have to be a BottomNavigationBar but you can actually pass any widget you'd like.
Here's the code (you can paste it into a file and run that file as it's self-enclosed):
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() => new MyAppState();
}
class MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
bottomNavigationBar: Stack(
children: [
new Container(
height: 100.0,
color: Colors.green,
),
Positioned(
left: 0.0,
right: 0.0,
top: 0.0,
bottom: 0.0,
child: new CustomPaint(
painter: Painter(),
size: Size.infinite,
),
),
],
),
),
);
}
}
class Painter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
canvas.drawLine(Offset.zero, size.bottomRight(Offset.zero), Paint());
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
// bad, but okay for example
return true;
}
}
I've just used a simple painter to illustrate the 'canvas' and how to position it the same as the picture, and a Container with a colour instead of a picture as I don't feel like adding assets to my project. The Positioned widget makes it so that the canvas can be sized to the image and not the other way around, as would happen if you just put them right into the Stack.
Note that if you don't want to use scaffold, you could probably also do this using a Column, with whatever you want for the body wrapped in an Expanded widget (as that will make it expand to fit as much space as possible, which should be everything except the space the image takes up).
you can use persistentFooterButtons widget inside Scaffold
#override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Demo App'),
),
body: _questionIndex < _questions.length
? Quiz(
list: _questions,
selectHandler: _printAns,
questionIndex: _questionIndex)
: Result(_totalScore, _resetQuiz),
persistentFooterButtons: <Widget>[
Row(
children: <Widget>[
RaisedButton(
onPressed: null,
child: Text('Prev'),
),
RaisedButton(
onPressed: null,
child: Text('Next'),
),
],
)
],
),
);