Hero animation starting from side menu drawer and ending on destination screen in Flutter - animation

I'm trying to do a Hero animation that starts from side menu drawer's item (it's Hero widget) and ends up on new screen's Hero widget. For example, on image shown below, a pie-chart icon should animate to the final widget when destination route is shown:
Each screen contains it's own scaffold with shared drawer widget instance:
Scaffold(
...
drawer: MyAppDrawer();
)
So for each of the screens and corresponding drawer items, I have the Hero tag on the screen that equals to the side menu drawer item Hero tag.
The destination screen is replacing the current screen like so:
Navigator.of(context).pushReplacementNamed("/");
After trying to push replacement screen on menu item action, instead of hero animation, I'm getting the following error:
There are multiple heroes that share the same tag within a subtree.
I guess it's because each of the screens already contains identical MyAppDrawer (with the same Hero tags)... But I can't omit the drawer from other screens because i need that menu to be globally accessible.
Is Hero animation from the common app side menu drawer item to destination screen Hero widget possible?
Any experiences with showing Hero animation from the drawer to the screen?
Thanks!

The problem is that you are not sharing the drawer with all the screens you are for each screen creating a new MyAppDrawer. Thinking fast here you could create the MyAppDrawer somewhere else and import the same object onto every screen

I have already facing this hero problem in my project.
If you put same hero tag like
tag:1 , tag :1, tag :1
then you will get this kind of error
tag:2 , tag:3 , tag:4
You will get your perfect solution through this trick.
and remembar that every hero tag must be unique in one screen.
And If you have using flotingActionButton then you have to put define tag inside button.

GestureDetector(
onTap: () => Navigator.push(
context,
PageRouteBuilder(
transitionDuration:
Duration(milliseconds: 700),
pageBuilder: (_, __, ___) => StoryInside(
tag: "Story $index",
))),
child: Hero(
tag: 'Story $index',
child: Material(
animationDuration: Duration(seconds: 20),
color: Colors.transparent,
child: ListTile(
title: Text(
'Story $index ',
style: style,
),
),
),
)),
Hero(
tag: '${widget.tag}',
transitionOnUserGestures: true,
child: Text(
'${widget.tag}',
style: style,
),
),

Related

Pin a one part on a Row to the screen while scroll the complete screen in Flutter

I am trying to build a layout explicit for Windows and Web. If the App becomes to small there will be another Layout.
The basic Idea is this Layout:
The green and the blue part are potential to big for the screen. And the red part is a header with some selection options.
My goal is, that the user can scroll the complete page down to the end of the green part. So that the red header will be disappear through scrolling down, the green part will be continued to be scrollable. So far I'd just use a Column in a SingleChildScrollView.
The tricky part is the blue part. I want it to get scrolled with the header to the top of the screen but then stays pinned there while the green part continues to be scrolled. Ideally it uses the complete height of the screen when the header is out of view.
Has somebody an idea how to achieve this?
Is there a ready solution I just don't know or would I need to exchange the layout when the header disappears?
If it helps. here is the code to make the basic layout:
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: [
//hides when scrolled down
_buildHeader(),
//some small divider
Container(height: 5, color: Colors.black,),
_mainContent(),
],
),
);
}
Widget _mainContent(){
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
//Container on the left is Fixed under header if header visible and uses complete screen if header is not visible
Expanded(child: _buildLeftContainer()),
//a Container with unknown hieght
Expanded(child: _buildRightContainer())
],
);
}
Widget _buildHeader(){
//some Content
return Container(
height: 150,
color: Colors.red,
);
}
Widget _buildRightContainer(){
//could be any height from not shown to 5 times the screens
return Container(
height: 1000,
color: Colors.green,
);
}
Widget _buildLeftContainer(){
// will contain a list of selectables with in specific sized pages
// height is for display, it should use the available space between on the left half of the screen
return Container(
height: 400,
color: Colors.blue,
);
}
Have you tried using Sliver widgets for this?
I haven't done exactly this, but I am pretty sure a combination of SliverList and SliverFillRemaining would work well for this.
You could have a large SliverList to handle the header disappearing, and put all the rest inside a SliverFillRemaining as a child of the large SliverList.
A video about SliverList, SliverGrid and SliverFillRemaining:
https://www.youtube.com/watch?v=k2v3gxtMlDE

flutter bloc and navigation drawer

New to Bloc and love having a structured app!
I used the firebase login example:
Flutter Firebase Login Tutorial
Works great and great example. Building upon this example I decided to do it step by step (avoid big mistakes).
I created a bottom navigation with an appbar that has a logout icon. Clicked on logout and it works.
Now I wanted to move the logout to the navigation drawer. I created a statelessWidget dart file called DrawerItems and then I call it from my bottom navigation file (within my scaffold). Here is part of the code of the DrawerItems:
DrawerListItem(
navListIcon: const Icon(Icons.logout_sharp),
strTitle: "Logout",
myFunction: () => context.read<AppBloc>().add(AppLogoutRequested()),
),
When I click on the logout icon, it doesn't do anything. Do I really need to make this statefulWidget? the whole point of bloc is to use stateful widgets as little as possible no?
any insight will be great!
Thank you in advance :D
Ok I figured it out... not really happy about this answer (I like having different files for everything), so if any of you know why this is happening feel free to drop a comment!
I removed the "myFunction" that I was passing and put the read bloc line directly in the drawer stateless class and it worked.
so this is my final code (I kept the old code there too FYI):
Widget build(BuildContext context) {
const drawerHeader = UserAccountsDrawerHeader(
accountName: Text("Account Name"),
accountEmail: Text("Account Email"),
currentAccountPicture: CircleAvatar(
child: FlutterLogo(size: 42.0),
),
);
return ListView(
children: [
drawerHeader,
// DrawerListItem(
// navListIcon: Icon(Icons.account_circle_sharp),
// strTitle: "Account information",
// ),
// DrawerListItem(
// navListIcon: Icon(Icons.filter_list_sharp),
// strTitle: "Filter",
// ),
ListTile(
leading: const Icon(Icons.logout_sharp),
title: const Text("Logout"),
onTap: () {
context.read<AppBloc>().add(AppLogoutRequested());
Navigator.pop(context);
},
),
// DrawerListItem(
// navListIcon: const Icon(Icons.logout_sharp),
// strTitle: "Logout",
// myFunction: () => context.read<AppBloc>().add(AppLogoutRequested()),
// ),
],
);
}

Flutter widget test Finder fails because the widget is outside the bounds of the render tree

Question: What controls the bounds of the "render tree" when running widget tests (flutter_test)?
I ask because I am getting an error on very basic button where it can't find the widget because of its vertical offset being outside the bounds of the "render tree" which seems fixed at 800x600.
I get the message:
Warning: A call to tap() with finder "exactly one widget with text "More Info" (ignoring offstage widgets): Text("More Info", dependencies: [MediaQuery, DefaultTextStyle])" derived an Offset (Offset(400.0, 641.8)) that would not hit test on the specified widget.
Maybe the widget is actually off-screen, or another widget is obscuring it, or the widget cannot receive pointer events.
Indeed, Offset(400.0, 641.8) is outside the bounds of the root of the render tree, Size(800.0, 600.0).
The finder corresponds to this RenderBox: RenderParagraph#1b6b1 relayoutBoundary=up27
The hit test result at that offset is: HitTestResult(HitTestEntry#b18dd(RenderView#408c3), HitTestEntry#a2393())
#0 WidgetController._getElementPoint (package:flutter_test/src/controller.dart:953:25)
#1 WidgetController.getCenter (package:flutter_test/src/controller.dart:836:12)
#2 WidgetController.tap (package:flutter_test/src/controller.dart:271:18)
#3 main. (file:///Users/tommy/Repos/surveyapp/survey/test/widget_test.dart:99:18)
The size of the render tree in that error message is 800 x 600. (Not sure how that is set or why. It is curious to me that it is exactly 800x600? So, it is being set somewhere. Is it because I have a web project and it defaults to that size for some reason when running a test?). Any widget that is past 600 on the offset height can't be found in the test. The screen runs fine on iOS emulator and on Chrome under flutter web. When you use devtools, widget inspector, there are not any layout issues. It is just a button with ancestors column, padding, center, safearea and scaffold as part of a simple stateful widget page.
(I have had this happening on Fluter version 2.12 or higher.)
I had the same problem when tapping a button during my tests.
I don't know how to change the 800x600 size but I manage to find a way to scroll to the button, thanks to this answer https://stackoverflow.com/a/67990754/992201
Here's the final code :
final Finder buttonToTap = find.byKey(const Key('keyOfTheButton'));
await tester.dragUntilVisible(
buttonToTap, // what you want to find
find.byType(SingleChildScrollView), // widget you want to scroll
const Offset(0, 50), // delta to move
);
await tester.tap(buttonToTap);
await tester.pump();
This issue was resolved for me by using following snippet
await tester.ensureVisible(find.byKey(Key(key)));
await tester.pumpAndSettle();
Note: This answer does not answer the question about the reason for the 800x600 but rather addresses the problem for fellow googlers.
I don't know why this happens (my best guess is some startup animation that is also simulated in the test), however adding await tester.pumpAndSettle() seems to fix the issue (hardening the assumption that this is caused by some animation).
I could not find any issues on the topic in the Flutter GitHub repository unfortunately.
So in summary, I believe the problem isn't caused by the 800x600 constraints but something else.
I had the same problem today.
The problem comes from the margin:
The following code was the button before fixing the problem:
Container(
color: Colors.green,
child: GestureDetector(
key: Key("resetPsw_back_arrow"),
child: Container(
margin: EdgeInsets.only(
top: myPercent(10, screenHeight),
right: (screenWidth - (myPercent(4.27, screenWidth) +
myPercent(13.1, screenWidth)) >
0
? (screenWidth -
(myPercent(4.27, screenWidth) +
myPercent(13.1, screenWidth))
: 0,
),
width: myPercent(4.27, screenWidth),
height: myPercent(4.27, screenWidth),
decoration: BoxDecoration(
color: Colors.red,
image: DecorationImage(
image: AssetImage("lib/media/img/forwardArrowMain.png"),
fit: BoxFit.contain)),
),
onTap: () {
if (Navigator.of(context).canPop()) {
Navigator.of(context).pop();
}
},
),
),
If you look carefully at the animation during the test, you can see that the tester is not taking into account the margin (it not tap on the button but next to it).
So I wrapped the GestureDetector in a container and the problem was solved.
This was the code after the fix:
Container(
margin: EdgeInsets.only(
top: myPercent(6, screenHeight),
right: (screenWidth - (myPercent(4.27, screenWidth) + myPercent(13.1, screenWidth))) > 0
? (screenWidth - (myPercent(4.27, screenWidth) + myPercent(13.1, screenWidth)))
: 0,
),
child: GestureDetector(
key: Key("resetPsw_back_arrow"),
child: Container(
width: myPercent(4.27, screenWidth),
height: myPercent(4.27, screenWidth),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("lib/media/img/forwardArrowMain.png"),
fit: BoxFit.contain)),
),
onTap: () {
if (Navigator.of(context).canPop()) {
Navigator.of(context).pop();
}
},
),
),

How to create a picture area in Flutter?

In their example https://flutter.dev/docs/cookbook/plugins/picture-using-camera we can take pictures using the camera plugin.
I modified the code and move into a separate widget. My main goal is to try to implement a picture area (like QRCode style) and take the picture and if necessary tell the user to corp the image or my be app will do it automatically. There is a barcode_scan plugin. It shows an area to scan the barcode. I like to implement that part to take pictures of an item.
https://codecanyon.net/item/qr-code-barcode-scanner-and-generator-for-ios-swift-with-admob/screenshots/20280521?index=1 screenshot2 has a 4 half square bracket on 4 edges. I like to make it similar but capture images only.
I am new to this plugin and corp idea. How can I create a picture area in Flutter so the user can center the item in that area and take a picture.?
Widget _buildPictureArea(BuildContext context) {
return new Container(
width: 200,
height: 200,
child: FutureBuilder<void>(
future: _initializeControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the Future is complete, display the preview
return CameraPreview(_controller);
} else {
// Otherwise, display a loading indicator
return Center(child: CircularProgressIndicator());
}
},
),
);
}

How to make Images Box with Price in Flutter

I am going to make an E-commerce application with price, image, discount view,
but I have no idea to make this view.
I have tried thousand ways but they gave me nothing.
Here are the preview:
Your question is too broad but it'll try to provide you with the necessary resources to achieve the layout you're looking for.
It all boils down to layout widgets (you can find the catalogue here: https://flutter.dev/docs/development/ui/widgets/layout ). There are single child and multiple children layout widgets. In order to create the discount tag effect you are going to need a Stack widget which as its name implies, it stacks its children on top of each other, you could then have and Image widget and the discount widget as children, effectively putting the discount label on top of the image. The layout for each card could be something like:
Container(
child: Column(
children: <Widget>[
Stack(
children: <Widget>[
Image(),
DiscountWidget(),
],
),
Text('Shirt'),
Row(
Text('Rp.50000'),
Text('345'),
),
Row(
RatingWidget(),
Text('Ready stock'),
),
],
),
);
You'd need to implement DiscountWidget and RatingWidget of course, but that code provides a high level overview of how you could create the layout you're looking for.
For line throuh
style: TextStyle(decoration: TextDecoration.lineThrough),

Resources