Flutter - Responsive Image mapping? - flutter

Good Evening,
I have been searching for many hours and can not find a solution.
I have set a full screen image and with a Stack I have Positioned several GestureDetectors.
I have succeeded to be able to press on a GestureDetector and call a Function.
The problem is that when the screen size changes, either to a new or older phone then the Image responds and covers the full screen but the Positioned() of course stay at the same place, thus the Image mappings are not correct any more.
Is there a way to make the Positioned be responsive? or Maybe a total different way achieving the desired outcome?
Please help me :)
class Overview extends StatelessWidget {
#override
Widget build(BuildContext context) {
double sh = MediaQuery.of(context).size.height;
double sw = MediaQuery.of(context).size.width;
return Scaffold(
drawer: AppDrawer(),
body: Stack(
children: <Widget>[
Container(
height: sh,
width: sw,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/ang7.png'),
fit: BoxFit.cover)),
),
Positioned(
left: 10,
top: 50,
child: IconButton(
icon: Icon(Icons.menu),
iconSize: 30,
color: Colors.white,
onPressed: () => Scaffold.of(context).openDrawer(),
),
Positioned(
left: left,
top: top,
child: GestureDetector(
// child: Icon(Icons.add_circle),
onTap: () => Navigator.push(context, MaterialPageRoute(
builder: (context) {
return Amenities(
tText[page][0],
tText[page][1],
tText[page][2],
);
},
)),
child: Container(
height: 35,
width: 50,
color: Colors.blue,
),
));
),

Posting my comment as an answer just in case it works and there are no better answers provided. Please up vote and mark as answered if it does. :-)
You could try experimenting with MediaQuery.of to determine the size of the device (height and width) and then use those values to scale the Positioned widgets position parameters up or down from your 'baseline' sizes. In theory that should (may) match how the background image has been scaled. Would probably need to scale the size of the Positioned widgets as well.
Update regarding your comment:
Not really. What I mean is that you would need to run MediaQuery.of on the device that you are happy with the UI layout, and record that device's width and height. You then use this width and height in your app and compare these 'baseline' values with what values you get when you run the app on other devices.
So:
Divide other-device-width by baseline-device-width to get your width scaling factor.
Divide other-device-height by baseline-device-height to get your height scaling factor.
Then use these scaling factors to 'scale' the Positioned widgets width (positioned-widget-width multiplied by width-scaling-factor) and height (positioned-widget-height multiplied by height-scaling-factor) and position on the screen (you would have to play around with the positioning values).
Sounds messy but it is just a bit of simple math.
Like I say, might not work or might not be the most elegant solution. There are so many different devices out there you will never really be able to completely test this. Might need to rethink you UI. Maybe some other people have ideas around that.

Related

Flutter: how to create widget positioned relative to the screen?

Basically I need to create some kind of Snackbar widget that can be created on any page. So I need to make it positioned relative to the screen. The only idea is to create the main Stack widget which will wrap all other pages. Do you have any other ideas? I found pretty similar question without any interesting answers
By using Mediaqueryto retrieve the screen size.
For example we can the screen width like this :
MediaQueryData screenSize = MediaQuery.of(context).size.width;
let's say i want my container's size to take up half the screen's size
One way to go about it is like this :
Container(
height: MediaQuery.of(context).size.height/2,
width: MediaQuery.of(context).size.width/2,
child: Text('This container is half this device screen's size');
);
What you are going for here is using the Positioned widget
Let's take the last example and leave a quarter of the screen on every side :
Container(
height: MediaQuery.of(context).size.height/2,
width: MediaQuery.of(context).size.width/2,
child: child: Stack(
children: <Widget>[
Positioned(
left: MediaQuery.of(context).size.width/4,
top: MediaQuery.of(context).size.height/4,
right: MediaQuery.of(context).size.width/4,
bottom: MediaQuery.of(context).size.height/4,
child: Text('This container is half this device screen's size'),
),
],
),

Flutter SizedBox vs LimitedBox

I have read docs for both SizedBox and LimitedBox, and didn't find any practical difference between the two.
Can anyone give an example when one may fail and other work?
So what I have found is, LimitedBox is only usable when the child is given unconstrained width/height by its parent.
And SizedBox simply creates a box with given width/height and doesn't allow child to go beyond given dimensions.
Example: (LimitedBox)
#override
Widget build(BuildContext context) {
return Scaffold(
body: LimitedBox(
maxHeight: 50, // no impact because it's child `Text` width/height isn't unconstrained
child: Text(
"A",
style: TextStyle(fontSize: 200),
),
),
);
}
Example (SizedBox)
#override
Widget build(BuildContext context) {
return Scaffold(
body: SizedBox(
height: 50, // has impact, it won't let Text to be of more than 50 logical pixels high
child: Text(
"A",
style: TextStyle(fontSize: 200),
),
),
);
}
Note: If anyone has better answer, I am happy to accept theirs.
LimitedBox is a SizedBox unless the parent widget imposes a constraint.
From Doc
A box that limits its size only when it's unconstrained.
Reference
Flutter doc
LimitBox:
LimitBox works only when it's size is unconstrained If this widget's
maximum width is unconstrained then its child's width is limited to
[maxWidth]. Similarly, if this widget's maximum height is
unconstrained then its child's height is limited to [maxHeight].
Example:
Center(
child: LimitedBox(
maxWidth: 50,
child: Container(
color: Colors.red,
width: 20,
height: 100,
)
),
),
Output:
This restricts Container Widget with a max-width as 50 but it's going to show widget till container width which is 20.
SizeBox:
It comes with fixed sizes that restrict its child to render on the
limited area. If either the width or height is null, this widget will
try to size itself to match the child's size in that dimension. If the
child's size depends on the size of its parent, the height and width
must be provided.
Example:
Center(
child: SizedBox(
width: 50,
child: Container(
color: Colors.red,
width: 20,
height: 100,
)
),
),
Output:
This will also behave like LimitedBox but the only difference here Container will be rendered till width 50 of SizeBox, it will not consider it's own width which is 20 and drawn till the parent widget.
Can anyone give an example when one may fail and other work?
They'll both work, but they'll do different things. The example from the LimitedBox documentation says:
This is useful when composing widgets that normally try to match their parents' size, so that they behave reasonably in lists.
Imagine that you are creating a reusable widget and don't have control over where it's used. You could use a SizedBox, but then your widget will always have the specified size, even if its size is already constrained by a parent widget.

Screen compatible to all devices in flutter

How to Adjust the screen to all android devices screens in flutter. I have checked in stackoverflow but unfortunately i haven't got any satisfying answer.
Basically, Flutter automatically sets the height and width depending on the device sizes wherever possible. Example - if you have use a list view having 100 items then some devices may show 5 items at a time and some may show 6 items. It is automatically done by Flutter.
The problem comes only when you specify an absolute value for height and/or width. Let's say you've a widget and you specify width as 450. Now, it may fit on the bigger screens but on the small screen (e.g. width 400 points)then you'll see pixel overflow error in the UI.
Now, to solve this problem, you can use MediaQuery.of(context).size.height/width as suggested by LGM.
I think you have some questions about responsive layouts, so i will give you some examples with MediaQuery:
With MediaQuery you can get the device screen width:
double width = MediaQuery.of(context).size.width;
The height:
double height = MediaQuery.of(context).size.height;
as well as the orientation:
Orientation orientation = MediaQuery.of(context).orientation;
and also various information about the device, and then, you can base the layout on orientation, size etc.
Here's an example:
Container(
width: MediaQuery.of(context).orientation == Orientation.portrait ?
MediaQuery.of(context).size.width / 1.3 : MediaQuery.of(context).size.width / 2.1,
child: RaisedButton(
elevation: 0.0,
color: Colors.green,
child: Text("CONTINUE", style: TextStyle(color: Colors.white),),
onPressed: (){
//code of onPressed
);
}
),
),
You can use too Align, to define where the widget should be, Positioned, the properties of Column, Center etc.
One more example, using FittedBox to deal with texts:
#override
Widget build(BuildContext context) {
return Material(
child: SafeArea(
child: Scaffold(
body: ListView(
children: <Widget>[
FittedBox(child: Text("Test"),),
FittedBox(child: Text("Flutter Flutter Flutter"),)
],
),
),
),
);
}
The result:
Portrait:
Landscape:

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

Different results when run on different screen sizes

I'm getting different results of the same screen when run on different phone sizes:
The second phone screen is my desired outcome. Using expanded initially helped me solve an issue with the first phone screen (where there was an extra space below the green bar). Later I realized that I was getting overflow on the second phone screen. Thus, I solved the overflow by using SingleChildScrollView. However that somehow cause the first phone screen to have the issue of the extra space again.
Code:
final quizBottomContentText = Container(
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.only(left:30.0, right:30.0, top: 30.0, bottom: 30),
child: Text(
questions[questionNum].title,
style: TextStyle(fontSize: 18.0),
)
);
final quizOptions = Container(
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.only(bottom: 20.0),
child: Center(
child: Column(
children: questions[questionNum].options.map<Widget>(
(option) => SimpleRoundButton(
backgroundColor: Color.fromRGBO(58, 66, 86, 1.0),
buttonText: Text(option,
style: TextStyle(
color: Colors.white
),
),
textColor: Colors.white,
onPressed: (){},
),
).toList(),
)
)
);
final countdown = CountdownWidget(
width: MediaQuery.of(context).size.width,
duration: 20,
triviaState: triviaState,
);
final quizBottomContent = Expanded(
child: SingleChildScrollView(
child: Container(
width: MediaQuery.of(context).size.width,
child: Column(
children: <Widget>[quizBottomContentText, quizOptions, countdown],
),
)
)
);
Because you're app is not responsive. See, you're passing size in a hard coded way
eg: padding: EdgeInsets.only(bottom: 20.0)
What this means? Mean that in your emulator, you'll get a result, maybe is what you expect, but in another device, maybe in a iPhone Xs Max, the result is diferent, so, what can you do?
You'll have two options here, the first, use the MediaQuery component. I'll show you how and why I use the way I use for a better understanding.
In Flutter, we have the MediaQuery component with a lot of propierty, one of them, is this: MediaQuery.of(context).size.width (Which gets your device's full width size). Everything fine till here, right? If you print this in diferent devices, you'll get diferente results, which means that a Padding with 20 from left/right will be different in both devices.
I made a calculation to kinda ''hack'' this and make it responsive, get this:
MediaQuery.of(context).size.width and divide by 400. Why 400? Me and a friend discovered this and when we divided, we've got a value almost next to 1 in very different devices, so, if you get the result (eg 1.5) and multiplies by 20, you'll make your app kinda of responsive. It's a way, the second way it's to use the LayoutBuilder which need a context and a constraints as parameters.
With constraints, you can manipulate to show differents model designs in differents devices, like, buildConventionalDesig for devices with width less than 400, an example, or buildBiggerDesign for a bigger phone, like iPhone Xs Max.
There's an article that you could be your reference to help you, check it:
Build Response UIs in Flutter. It doesn't cover this trick with MediaQuery, but maybe you can get a new insight.
Hope this have helped.