Flutter - Custom Paint Animation disappears from screen - flutter

I have some thumbnails with a custom paint animation, and i want to show the selected animation at the main container when the user tap the thumbnail.
This part is "ok". The problem is that the custom paint disappears a few seconds after ends the animation. And i'm setting the same widget from the thumbnail to the main container, but differently of the thumbnail, the custom paint doesn't remains after the animation ends on the main container. Someone has any idea of what can be the problem?
Here is a gif with what is happening.

The problem was related to the same question of this answer, but I couldn't figured it out because even the thumbnails are inside the stack. And I was getting the problem even setting the width and height for the parent container of the custom paint. I solved only wrapping the entire stack with a LayoutBuilder like the code bellow:
_buildContent() {
var ctxSize = MediaQuery.of(context).size;
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Stack(
children: <Widget>[
//Code with CustomPaint
],
);
},
),
);
}

Related

How to wrap height according to child content for showModalBottomSheet or remove auto height

I have one bottom sheet and under that bottom sheet i am managing routing
my each screen have different screen size. and i want to change the height of showModalBottomSheet for each screen.
showModalBottomSheet<void>(
context: context,
isDismissible: true,
//isScrollControlled: true,
elevation: 2.0,
barrierColor: Colors.white.withOpacity(0),
builder: (BuildContext context) {
// double ht = Provider.of<SearchByLocationNotify>(context).getScreenSize;
return SizedBox(
height: MediaQuery.of(context).size.height * 0.5,// currently static height provided
child: PopUpHome());
},
);
change the height according to each screen
or wrap the content of children so that static height is not needed
In the above image red porion is showModalBottomSheet blue is my child so i want red portion should only occupy blue.
return a Column with mainAxisSize: MainAxisSize.min from the builder and place your content inside the Column widget.

Keep button fixed in the bottom of screen, but only when the above list height is shorter than the device height

I am developing a Flutter app and would like to keep my button always in the bottom of the screen UNLESS what is in top of it is high enough to make the screen scroll. Examples:
Example A (what is on top of the button does not have height enough to scroll the screen)
Example B (what is on top of the button has height enough to scroll the screen, so the button will just go offscreen and allow the screen to scroll)
So summarizing: the button should be forced to stay at the bottom of the screen when the rest of the list is shorter than the device height, but if the list height is greater than the device height the button should behave normally and stay below the list.
What I tried:
Using a ListView, which will normally scroll if necessary, but I couldn't find a way to send the button to the botton of the screen;
Using a Column. With the help of a Spacer I could make the button go to the bottom of the screen, but a Column will not scroll, and if I add a SingleChildScrollViewto wrap it, the Spacer will no longer work because SingleChildScrollView has the potential to have infinite height;
Thanks.
I got it. The right way of doing this is using LayoutBuilder, ConstrainedBox, IntrinsicHeight and Expanded, like this:
(In this example, _widgets is a list of widgets that I want it to be on top. _buttons is a list of buttons that I want to be in the bottom)
return Scaffold(
appBar: buildAppBar(),
body: LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: IntrinsicHeight(
child: Column(
children: <Widget>[
Expanded(
child: Column(
children: _widgets,
),
),
Column(
children: _buttons,
)
],
),
),
),
);
},
),
);
The documentation says to avoid this, though, as it can be expensive. You can read more about this solution and other details in the documentation.
Stack(
children:[
//Use list widget,
yourList.length<7? Postioned(
bottom :10, margin from bottom
child: //button widget):Container()
])
In listbuilder
ListView.builder(
physics: ScrollPhysics(),
itemCount: yourList+1,
itemBuilder: (BuildContext ctxt, int index) {
if (index== yourList.length) {
return yourList.length >7? //your button widget:Container();
} else {
return //list item}
});

How to get inside a widget the resulting size of itself shrinked by FittedBox

In the code below, a row of two 300x300 boxes (_Box) wrapped with FittedBox shrinks to fit in the screen. As a result, each box becomes smaller than 300x300.
However, if I get the width of a box in the build() method of _Box using RenderBox.size and LayoutBuilder(), the obtained size is 300.0 and Infinity respectively.
How can I get the actual displayed size?
I'd like to get it inside the _Box class, without it getting passed from the parent.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: FittedBox(
child: Row(
children: <Widget>[
_Box(Colors.red),
_Box(Colors.orange),
],
),
),
),
),
);
}
}
class _Box extends StatelessWidget {
const _Box(this.color);
final Color color;
#override
Widget build(BuildContext context) {
RenderBox renderBox = context.findRenderObject();
print(renderBox?.size?.width); // 300.0
return LayoutBuilder(
builder: (_, constraints) {
print(constraints.maxWidth); // Infinity
return Container(
width: 300,
height: 300,
color: color,
);
},
);
}
}
The Flutter constraints object is used to limit how large or small a widget can be rendered, and is usually never really used to get the current size of the widget.
The renderBox.size object is of Size class, and as a result it has both renderBox.size.width and renderBox.size.height as defined getters. Note that these values can only be set once the layout phase of the current view is over: see the findRenderObject() docs page.
This means that you will have to avoid calling findRenderObject() from the build() method. Instead you will have to define a callback function that must execute after the layout process is complete. You can do this using that have widgets that have callback functions like onTap or onSelected. How you implement this and finally get the actual layout size by running the callback function is totally dependent on your use case.
Further recommended reading:
DevelopPaper - Sample code for getting the width and height of screen and widget in Flutter
Flutter on Github - How to get a height of a widget? #16061
Rémi Rousselet's amazing answer explaining his workaround (using an Overlay widget)
I'll answer my own question, although it is not a direct answer.
I couldn't find a way to get the size shrinked by FittedBox, but I realised that I was able to get around it by using Flexible instead.
SafeArea(
child: Row(
children: const [
Flexible(
child: _Box(Colors.red),
),
Flexible(
child: _Box(Colors.orange),
),
],
),
);
#override
Widget build(BuildContext context) {
return ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 300.0),
child: AspectRatio(
aspectRatio: 1.0,
child: ColoredBox(color: color),
),
);
}
It still seems impossible to get the size via RenderBox, and it is now possible with LayoutBuilder. But either way, I didn't need them.
The constraints of the two boxies are shrinked by Flexible if a smaller space is available, but they expand as big as the space allows, so I limited the maximum size using ConstrainedBox and AspectRatio.
I didn't have to stick to FittedBox. I think I was obsessed with the idea of using it and couldn't think of other solutions when I posted the question two years ago.

How to make Hero animation for two widgets

I need to make animation like Hero in two different widgets. Is that possible?
Widget a with image a and widget b with image a but widget a is inside a listview and widget b is full screen image(it hide listview)
It's very simple. You just need to wrap both widgets in a Hero widget using the same TAG property value. The snippet below is assuming you've a Image in a ListTile and after user clicks you show a new page with the same image but that image will be animated by Hero widget.
In list page the list items can be
ListTile(
leading: Hero(
tag: IMAGE_TAG, // must be the same in different pages
child: Image(image: AssetImage('you_asset_dir/yourImage.png')),
),
title: Text('Anything'),
onTap: () => // Just go to full screen page
);
In full screen page
Scaffold(
body: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Hero(
tag: 'IMAGE_TAG', // must be the SAME VALUE DEFINED IN list tile hero child widget.
child: Image(image: AssetImage('you_asset_dir/yourImage.png')),
),
),
);
OBS: The Hero tag property must be the same in different contexts but if you have a list with many items using Hero widget each Hero int the same context/page must have different tag values.
To deep dive concepts about Hero animations check the official doc

Flare and Flutter with different devices (screens)

i am doing my first steps with flare and flutter and right now its really nice to be able to put animations into flutter without coding them by hand. But i dont understand how to make the flare thingy responsive (how to support different screen sizes).
This is part of a splash screen:
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color.fromRGBO(250, 224, 61, 1.0),
body: Center(
child: Container(
child: FlareActor(
"assets/flare/splash.flr",
callback: (nameOfAnimation) =>
Navigator.pushReplacementNamed(context, "/login"),
fit: BoxFit.none,
animation: "splash",
)),
));
}
This works well on my iPhone X because the animation is designed for that size. Is there any way how a smaller device can be able to use this same flare animation? Testing this with iPhone SE resulted in a clipped animation.
I hope there is another way than creating several animations for several screen sizes.
Just add your animation as a child of a SizedBox and give it a width/height and you’ll be fine.
You can also use the MediaQuery.of(context).size.width or height to get the viewport dimensions and set your SizedBox to use a % of the screen accordingly, if you want to.
you can use MediaQuery with the current context of your widget and get width or height in your container or sizedbox like this
width: MediaQuery.of(context).size.width * 0.65
height: MediaQuery.of(context).size.width * 0.65
else If you have a single widget you can use a FractionallySizedBox widget to specify a percentage of the available space to fill
FractionallySizedBox(
widthFactor: 0.7,
heightFactor: 0.3,
child:FlareActor(
"assets/flare/splash.flr",
callback: (nameOfAnimation) =>
Navigator.pushReplacementNamed(context, "/login"),
fit: BoxFit.none,
animation: "splash",
)),
);