I have a set of x/y coordinates which I'd like to use to show widgets on top of an Image widget at those specific coords, relative to the original image dimensions (i.e. coords of 100/100 would show a widget at 100/100 on the image).
I'm currently using a Stack containing the Image and some Positioned widgets, but I can't get them to display in the right place. The positioned widgets aren't displaying in the correct position on the image, possibly because of the image scaling?
Any ideas how I could achieve this? Thanks
For some context, the image is a map and the positioned widgets are pins on the map
Code for the pin widget (top/left is y/x):
Positioned(
top: top,
left: left,
child: GestureDetector(
child: IconTheme(
child: Icon(Icons.location_pin),
data: IconThemeData(
color: available ? Colors.green : Colors.red,
),
),
onTap: () => print("Selected space $spaceId"),
),
);
Code for the interactive viewer + stack:
transformationController: widget._controller,
maxScale: 4.0,
minScale: 0.2,
child: Stack(
children: [
Image.network(
"https://i.pinimg.com/736x/bd/d6/f5/bdd6f5247dbea0e5eedf33fe8cc491ee--office-layout-plan-office-floor-plan.jpg",
fit: BoxFit.cover,
alignment: Alignment.center,
height: double.infinity,
width: double.infinity,
),
...getSpacePins()
FutureBuilder<List<SpacePin>>(
future: spaces,
builder: (context, snapshot) {
if (snapshot.hasData)
return snapshot.data![1];
else if (snapshot.hasError) return Text("Error");
return const CircularProgressIndicator();
},
)
],
),
)
The method getSpacePins() returns a list of pin widgets in to the stack.
I don't have the reputation to directly comment. I've got the same problem but haven't solved it 100% yet, nor found anyone who has the answer. For this place the Image in a Stack Widget. Place layers of pins by looping through your list of pins. Wrap the pins in a positioned widget. The Issue I have here is the X and Y are correct and the math I have is correct but its off set by things like the app bar height and other padding in the scaffold. I can't seem to apply it to the X, Y of the Image on the stack.
for (Pins pin in listOfPins)
Positioned(
top: (pin.y ?? 0) * constraints.maxHeight,
left: (pin.x ?? 0) * constraints.maxWidth,
child: Image.asset(
DRAWINGS_PINS_PATH,
height: 40,
width: 40,
)
Related
I am trying to get a text label with graphics over the image. Labels like new arrival, best seller, limited, on sale just like on the pic attached.
enter image description here
Here's what i think the skeletal layout would be :
return Stack(
children: [
Card(
color: Colors.black54,
child: Image.asset('assets/xyz.png'),
),
Align(
//for bottomRight
alignment: Alignment.bottomRight,
child: Image.asset(
'assets/fireimg.png',
), //can be an SVG converted to png
),
Align(
//for topLeft
alignment: Alignment.topLeft,
child: Image.asset(
'assets/label.png',
), //can be an SVG converted to png
),
],
);
Feel free to give the Padding of your choice.
it has so much code to write , so i will give some examples to achieve that output
in this image ,i mentioned 0 and one .
for all 1
solution 1
you can use image like this way
Stack(
children: [
Container(
width: 200,
height: 200,
color: Colors.amber,// added only for view this example
child:Image() // add Product image here
),
Positioned(
top: 20, //change according to your use case position
left: 0,// change according to your use case position
child: Container(
color: Colors.black, // added only for view this example
decoration: BoxDecoration(image: ),// add label image here
width: 100,
height: 50,
child: Row(
children: [Text('hi')],
),
))
],
)
solution 2
you can use custom paint for label layout
not that
in both solution you have to use stack and position widgets , you can control position according to your use case
for all 0
you can use RotationTransition widgets to achieve this
not that
you have to use stack and position widgets , you can control position according to your use case
for the vertical Text
use this example
String exText = "meta";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('hi'),
),
body: Container(
width: 200,
height: 200,
color: Colors.amber,
child: Column(
children: exText.characters
.map((e) => Container(
child: Text('$e'),
))
.toList()),
),
out put will be
I have 2 widget in stack. One of them is aligned in center. And the widget which is on the top is other stack. And I want show second widget with first widget's top and left position. Here is the explain:
The yellow area is my stack. And first widget is setted like Center(child: myFirstWidget). My second widget is referenced from here it's a resizable widget so it's an another stack and it's childs are "positioned". So I need to set top and left value for initialize. But my main stack filled page So when I set my second widget's top and left to 0. It's shown as below.
But I want to show align it to centered child's top like:
My code snip:
child: Container(
color: Colors.red,
child: Stack(
children: [
Center(
child: Image.file(
File("myfilepath"),
),
),
ResizableWidget(
child: Container(
color: Colors.blue,
),
),
],
),
),
You can adjust the position of the widgets with the Position widget like this:
Positioned(
child: ResizableWidget(
child: Container(
color: Colors.blue,
),
),
top: 80,
right: -5,
),
The value of top and right are just random values, you should check what works for you!
You should make Center the parent of the Stack.
The Stack will be positioned at the center of its parent because of Center, it will get the dimensions of its biggest childs (height and width) and will position other childs according to the alignment value.
Edit : I modified the code to include a placeHolder image. Here I gave it a 6000x2000 size but you can change it to whatever value you want. Code is working as expected, see capture below.
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Stack(
alignment: AlignmentDirectional.topStart,
children: [
Image.network('https://via.placeholder.com/6000x2000'),
SizedBox(
height: 50,
width: 50,
child: Container(
color: Colors.green,
))
],
),
),
);
}
I would like to have a banner widget, probably a VideoPlayer (from the video_player plugin) or else just an image.
Depending on the size of the screen/window I want my banner to follow like this:
https://i.imgur.com/YADZSrV.mp4
Imagine that the scaling in the video is the window size changing.
Basically:
If aspect gets wider than the original -> show less on top and bottom (kinda zooming in)
If aspect gets taller than the original -> show less on the sides (kinda cropping while centering)
I got something to work partially. It works when making the window wider, but when it gets slimmer it just starts to scale everything down, it doesn't keep the full height while showing less on the sides.
Here is my work in progress:
return ClipRect(
child: OverflowBox(
maxWidth: double.infinity,
maxHeight: double.infinity,
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: VideoPlayer(_controller),
),
),
);
This can be solved very easily by using the FittedBox widget and the BoxFit enum.
FittedBox(
fit: BoxFit.cover,
child: Container(
width: 960,
height: 360,
child: VideoPlayer(_controller),
),
)
Using the VideoPlayer widget as a child of a Container where I set the size to the original size of the video.
Example using an image:
FittedBox(
fit: BoxFit.cover,
child: Image(
image: NetworkImage(
'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg'),
),
)
You can use LayoutBuilder to get the size of its content and render differently accordingly:
https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html
In you case, it would be something like this :
return ClipRect(
child: OverflowBox(
maxWidth: double.infinity,
maxHeight: double.infinity,
child: LayoutBuilder(
builder: (context, constraints) {
if (constaints.bigger.width > constaints.bigger.height) {
return Text('More width than height');
}
else {
return Text('More height than width');
}
}
)
),
);
Assume we have an empty parent widget (a Container), with no size, and positioned absolutely (within it's own parent) at a top/left (x/y) coordinate. Then we place a child widget (with variable size, which is not known beforehand) within the parent. Is there a way to position the child at -50% of its own width, such that the child's horizontal center appears at the x coordinate of its parent? I don't want to use global keys as there will be hundreds of instances. I've tried Transform.translate (only accepts a known x offset), FractionalOffsets, FractionallySizedBoxes, Alignments etc, but nothing seems to do what I'm trying to achieve. Does anyone know of a solution?
Edit: image demonstrating the objective
As per #pskink's comment, I was able to achieve this by combining a Positioned and a FractionalTranslation
Positioned(
left: childWidget.x,
top: childWidget.y,
child: FractionalTranslation(
translation: Offset(-0.5, -0.5),
child: childWidget,
)
);
You can use AlignPositioned widget from align_positioned package.
AlignPositioned(
alignment: Alignment.topLeft,
moveByChildWidth: -0.5, //negative 50% of child width
child: Icon(
Icons.info,
size: 200,
),
),
Edit:
Widget build(BuildContext context) {
return Stack(
children: [
Transform.translate(
offset: Offset(100, 200),
child: Container(
child: AlignPositioned(
alignment: Alignment.topLeft,
moveByChildWidth: -0.5,
child: Icon(
Icons.info,
size: 100,
),
),
),
),
],
);
}
Gives the same result without the Container too i.e. making Transform.translate itself as the parent.
Result:
Is there a way to implement a rotated by 90 degrees container that will fill the space available inside a Stack? When I try to set the size of the child of the rotated widget it seems like it is still being limited by the parent widget. I would like to know if there is someway to make it work.
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: <Widget>[
// something with size to define the size of the stack
Container(
color: Colors.white,
height: 600,
width: 300,
),
Positioned(
child: LayoutBuilder(builder: (context, constraints) {
return SizedBox(
width: constraints.maxHeight,
height: constraints.maxWidth,
child: Transform.rotate(
angle: math.pi / 2,
// this should have height equal to constraints.maxWidth
// and width equal to constraints.maxHeight
// but the height is equal to constraints.maxWidth
// and the width as well
child: Container(color: Colors.black.withOpacity(0.5)),
),
);
}),
),
],
);
}
You can fix this by using an OverflowBox instead of a SizedBox
See difference between a SizedBox and OverflowBox below:
SizedBox
A box with a specified size.
If given a child, this widget forces its child to have a specific width and/or height (assuming values are permitted by this widget's parent).
OverflowBox
A widget that imposes different constraints on its child than it gets from its parent, possibly allowing the child to overflow the parent.
I hope this helps.
I just figured there is a widget called OverflowBox that can be used to get this behavior. I swapped the SizedBox with a OverflowBox widget and things started to work as expected.
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: <Widget>[
// something with size to define the size of the stack
Container(
color: Colors.white,
height: 600,
width: 300,
),
Positioned(
child: LayoutBuilder(builder: (context, constraints) {
return OverflowBox(
maxWidth: constraints.maxHeight,
maxHeight: constraints.maxWidth,
child: Transform.rotate(
angle: math.pi / 2,
// has the expected size
child: Container(color: Colors.black.withOpacity(0.5)),
),
);
}),
),
],
);
}
RotatedBox(
quarterTurns: _rotateAngel,
child: _yourChildWidget());