I do not know what is wrong with this image loading inside the slider , also I thought this has something to do with the debug but the release version has the same problem, overall all the pictures load slowly and have some delay but this problem is worse.
Container(
margin: EdgeInsets.only(top: Platform.isAndroid ? 85.0 : 115.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CarouselSlider(
aspectRatio: 1.1,
viewportFraction: 0.6,
initialPage: 0,
enlargeCenterPage: true,
reverse: false,
autoPlay: false,
enableInfiniteScroll: false,
scrollDirection: Axis.horizontal,
onPageChanged: (index) {
if (!mounted) return;
setState(() {
_current = index;
SystemChrome.setEnabledSystemUIOverlays(
[SystemUiOverlay.bottom]);
});
},
items: [
slides("words", "assets/images/Academic_Words.jpg", "Academic\n Words", MyAppWords()),
slides("writing", "assets/images/writing.jpg", "Academic\n Writing", MyAppWriting()),
slides("conference", "assets/images/conference.jpg", "Academic\nConference", EnterPhone()),
slides("conversation", "assets/images/conversations.jpg", "Academic\nConversations", null),
slides("correspondence", "assets/images/correspondence.jpg", "Academic\nCorrespondence", MyAppCorrespondence()),
].map((imgUrl) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
alignment: Alignment.center,
margin: EdgeInsets.symmetric(
vertical: 20.0, horizontal: 9.0),
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(48.0),
boxShadow: [
BoxShadow(
color:
Color.fromRGBO(50, 50, 50, 1),
blurRadius: 5.0,
// has the effect of softening the shadow
spreadRadius: 5.0,
// has the effect of extending the shadow
offset: Offset(
-1.0,
// horizontal, move right 10
8.0, //
// vertical, move down 10
),
)
]),
child: ClipRRect(
borderRadius: BorderRadius.circular(48.0),
child: imgUrl,
));
},
);
}).toList()),
]),
decoration: BoxDecoration(
borderRadius:
BorderRadius.vertical(top: Radius.circular(48.0)),
color: Color.fromRGBO(237, 237, 237, 1),
),
),
]);
},
));
}
slides(String _tag,String _asset, String _title, Widget myF ){
return GestureDetector(
child: Stack(
fit: StackFit.expand,
alignment: Alignment.center,
children: <Widget>[
Hero(
tag: _tag,
child: Image.asset(
_asset,
fit: BoxFit.cover,
),
),
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.black45,
Colors.black54
],
begin: Alignment.topLeft,
end: Alignment.bottomRight
),
),
),
Container(
alignment: Alignment.center,
child: Text(
_title,
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.w400,
fontFamily: "Raleway"),
),
)
],
),
onTap: () => Navigator.push(context,
MaterialPageRoute(
builder: (BuildContext context) {
return myF;
})),
);
}
here is a demonstration of my problem=> http://uupload.ir/view/k2ub_video_2019-10-22_10-14-47.mp4/
The behavior seems to be caused by the scaling of the large image. Since carousel_slider: 2.0.0, it's recommended to pass CarouselOptions to options - wherein you can define either the height or aspect ratio for the carousel items.
Related
I am using CarouselSlider in Flutter to get the output as below (Special Event section):
But getting result as below :
The Issue is It should be with same width as top and bottom widget vertically (you can see in first image), In result Image, there is little more width between right and left transparent area and middle portion. So, middle portion width and the transparency of left and right edge is concern here.
How can I get the same result?
I have done so far as below:
Container(
child: CarouselSlider(
options: CarouselOptions(
enlargeCenterPage: true,
disableCenter: false,
scrollDirection: Axis.horizontal,
onPageChanged: (index, reason) {
setState(() {
activeSpecialEventPage = index;
});
}),
items: <Widget>[
for (var i = 0; i < special_events.length; i++)
GestureDetector(
onTap: () async {
await getCurrentLocation();
if (getDouble(prefCurrLat) != null &&
getDouble(prefCurrLong) != null) {
NavigationUtils.push(context, routeDetailScreen,
arguments: {
argDetailScreenTitle:
Localization.of(context).labelExhibitions,
argCurrentLat: getDouble(prefCurrLat),
argCurrentLong: getDouble(prefCurrLong),
argEventObj: special_events[i]
});
}
},
child: Container(
width: MediaQuery.of(context).size.width,
child: Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10.w),
topRight: Radius.circular(10.h),
bottomRight: Radius.circular(10.w),
bottomLeft: Radius.circular(10.h)),
child: Image.network(
special_events[i].image.toString(),
errorBuilder: (context, url, error) => Center(
child: SizedBox(
width: 160.w,
height: 160.h,
child: Image.asset(imgPlaceHolder))),
loadingBuilder: (BuildContext context,
Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) {
return child;
}
return Center(
child: Image.asset(imgPlaceHolder,
width: 160.w,
height: 160.h,
fit: BoxFit.cover),
);
},
width: 327.w,
height: 200.h,
fit: BoxFit.cover)),
Positioned(
bottom: 16.h,
left: 20.w,
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
color: blackColorOP11,
width: 300.w,
child: Text(
special_events[i].name.toString(),
style: TextStyle(
fontWeight: FontWeight.w600,
fontFamily: "Poppins",
fontSize: 24.sp,
color: Colors.white),
softWrap: false,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
special_events[i].dateText != null &&
special_events[i]
.dateText
.toString()
.length >
0
? Container(
color: blackColorOP11,
child: Text(
getFormatedDateForSpecialEvent(
special_events[i]
.dateText
.toString()),
style: TextStyle(
fontWeight: FontWeight.w400,
fontFamily: "Poppins",
fontSize: 12.sp,
color: Colors.white),
softWrap: false,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
)
: Container(),
],
),
),
)
],
),
),
)
],
),
)
: buildNoDataWidget(Localization.of(context).labelNoSpecialEvents);
There are a number of variables you can play with here. I've assumed you've used the carousel_slider package.
In the CarouselOptions you can change the aspectRatio and viewportRatio and resolve your issue right away. For example a viewportFraction: 0.7 would work for your code sample.
However if you want your viewPort to remain the same, you would have to change the width and height of your Image.network AND the aspectRatio to something like:
- CarouselOptions
aspectRatio: 20 / 9
Image.network
width: 360,
height: 200
If there's a specific height or width you must strictly abide to you should play with these 3 values to ensure it work.
I've tested the below code, with some alterations, with dummy data and values and they work well together.
class SpecialEvents {
final String image;
final DateTime dateText;
final String name;
SpecialEvents(
{required this.image, required this.name, required this.dateText});
}
List<SpecialEvents> special_events = [
SpecialEvents(
name: "Road",
image:
"https://images.unsplash.com/photo-1666069810128-e7dfe3b0d653?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=692&q=80",
dateText: DateTime.now()),
SpecialEvents(
name: "NY",
image:
"https://images.unsplash.com/photo-1665806558925-930b7210d8bb?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80",
dateText: DateTime.now())
];
CarouselSlider(
options: CarouselOptions(
enlargeCenterPage: true,
aspectRatio: 20 / 9,
// viewportFraction: 0.7,
// padEnds: false,
disableCenter: false,
scrollDirection: Axis.horizontal,
onPageChanged: (index, reason) {
setState(() {
activeSpecialEventPage = index;
});
}),
items: special_events
.map((SpecialEvents se) => GestureDetector(
onTap: () async {
debugPrint("tap function");
},
child: ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
bottomRight: Radius.circular(10),
bottomLeft: Radius.circular(10)),
child: Stack(
children: [
Image.network(se.image.toString(),
errorBuilder: (context, url, error) =>
Center(
child: SizedBox(
width: 160,
height: 160,
child: Image.asset(
"imgPlaceHolder"))),
loadingBuilder: (BuildContext context,
Widget child,
ImageChunkEvent?
loadingProgress) {
if (loadingProgress == null) {
return child;
}
return Center(
child: Image.asset(
"image place holder URL",
width: 160,
height: 160,
fit: BoxFit.cover),
);
},
width: 360,
height: 200,
fit: BoxFit.cover),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.end,
children: [
Row(
children: [
Expanded(
child: Container(
color: Colors.black26,
width: 300,
child: Padding(
padding: const EdgeInsets
.symmetric(
vertical: 8.0,
horizontal: 20),
child: Column(
crossAxisAlignment:
CrossAxisAlignment
.start,
children: [
Text(
se.name.toString(),
style:
const TextStyle(
fontWeight:
FontWeight
.w600,
fontSize: 24,
color: Colors
.white),
softWrap: false,
maxLines: 1,
overflow: TextOverflow
.ellipsis,
),
Text(
se.dateText
.toString(),
style:
const TextStyle(
fontWeight:
FontWeight
.w400,
fontSize: 12,
color: Colors
.white),
softWrap: false,
maxLines: 1,
overflow: TextOverflow
.ellipsis,
)
],
),
),
),
),
],
),
],
)
],
)),
))
.toList())
Hope that helps explain it.
There is a property on CarouselSlider, onPageChanged.
you need to maintain a int, e.g. currentPageIndex as below,
...
onPageChanged: (index, _) {
setState(
() {
currentPageIndex = index;
}
);
}
...
And then inside itemBuilder do like this,
...
itemBuilder: (context, index, _) {
return Opacity(
opacity: index == currentPageIndex ? 1.0 : 0.2,
child: Your_Widget(),
);
}
...
I have an app that has pageView.builder and it contains 5 stack widget
how can I scale up the widget that user-selected, for example:
if the user scrolls to widget 3, widgets 1 & 2 become smaller than widget 3, and the same if he scrolls to widget 5 or 2
( the middle will become bigger than the widget on both sides)
my code :
Widget build(BuildContext context) {
// ignore: sized_box_for_whitespace
return Container(
height: 320,
child: PageView.builder(
controller: pageController,
itemCount: 5,
itemBuilder: (context, position) {
return _bulidPageItem(position);
}),
);
}
Widget _bulidPageItem(int index) {
//------------------------------------------------------------------------------
// Slide image 🚩
return Stack(
alignment: Alignment.topCenter,
children: [
Container(
margin: const EdgeInsets.only(left: 5, right: 5),
height: 220,
width: 350,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: index.isEven
? const Color(0xFFffd28d)
: const Color(0xFF89dad0),
image: const DecorationImage(
image: AssetImage('images/chineseFood.jpg'), fit: BoxFit.cover),
),
),
//------------------------------------------------------------------------------
// Slide Information 🚩
Align(
alignment: Alignment.bottomCenter,
child: Container(
margin: const EdgeInsets.only(left: 30, right: 30, bottom: 15),
height: 130,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.3),
blurRadius: 6,
spreadRadius: 0.7,
offset: const Offset(1, 4))
],
borderRadius: BorderRadius.circular(25),
color: Colors.white,
),
//------------------------------------------------
// Slider title
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const BigText(
text: 'Chinese side',
),
//----------------------------------------------
// Slider Rating
const SizedBox(height: 10),
Row(
children: [
Wrap(
children: List.generate(
5,
(index) => const Icon(Icons.star,
color: AppColor.mainColor, size: 12),
),
),
const SizedBox(width: 10),
SmallText(text: 4.5.toString()),
const SizedBox(width: 10),
const SmallText(text: '1287 comments'),
],
),
const SizedBox(height: 20),
//----------------------------------------------
// Slider Icons
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: const [
SliderIcons(
color: AppColor.iconColor1,
text: 'Normal',
icon: Icons.circle),
SliderIcons(
color: AppColor.mainColor,
text: '1.7km',
icon: Icons.location_pin),
SliderIcons(
color: AppColor.iconColor2,
text: '32min',
icon: FontAwesomeIcons.clock),
],
),
],
),
),
),
),
],
);
}
}
I think the carousel slider package provides the functionality you need. The package has an example, which exactly describes your issue.
you should try out https://api.flutter.dev/flutter/widgets/ListWheelScrollView-class.html
and for your layout, you can use rotate widget for the horizontal view and of course
you to use rotate widget on the child of the list too.
Save the current page and adjust the content based on that. Like below the current page, _currentPage == index, has smaller margins.
final _pageController = PageController(viewportFraction: 0.8);
int _currentPage = 0;
...
PageView.builder(
controller: _pageController,
itemCount: 5,
itemBuilder: (_, index) =>
Container(
margin: _currentPage == index
? const EdgeInsets.symmetric(vertical: 16)
: const EdgeInsets.symmetric(vertical: 64),
child: Image.network(
'https://picsum.photos/1080/1920?index=$index',
fit: BoxFit.cover,
loadingBuilder: (_, child, loadingProgress) {
if (loadingProgress == null) return child;
return const Center(child: CircularProgressIndicator());
}
)
),
onPageChanged: (page){
setState(() {
_currentPage = page;
});
},
)
)
I have a flutter app. One of my screens is loading lots of images from assets. Sometimes my app is freezing while the images are loading.
Here is my solution: I will put a loading animation, this animation needs to end up after the images are finished rendering but how do I understand all the images are finished rendering?
If you have any other solutions you can share with me :)
here is the Widget tree that loads images, in case if you need to know.
final posts = [
Post(
ownerName: "McQueen95",
avatarPath: "images/mcqueen.jpg",
imagePaths: ['images/mcqueen.jpg'],
),
Post(
ownerName: "BugsBunny",
imagePaths: ['images/Bugs_Bunny.png'],
),
Post(
ownerName: "Venom",
imagePaths: ['images/venom.jpg'],
),
Post(
ownerName: "Po",
imagePaths: ["images/kungfu_panda.jpg"],
avatarPath: "images/po.jpg",
),
Post(
ownerName: "Po",
imagePaths: ["images/kai.jpg", "images/oogway.jpg", "images/crane.png"],
avatarPath: "images/po.jpg",
),
];
#override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: [
SliverAppBar(
title: Text("Cossy", style: TextStyle(color: Colors.purple)),
// elevation: 0,
pinned: true,
// expandedHeight: 150.0,
backgroundColor: backgroundColor,
systemOverlayStyle: SystemUiOverlayStyle(
systemNavigationBarColor: backgroundColor,
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => Padding(
child: posts[index],
padding: EdgeInsets.only(
bottom: (index == posts.length - 1) ? 82 : 12,
),
),
childCount: posts.length,
),
)
],
);
}
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_image_slider/carousel.dart';
class Post extends StatelessWidget {
Post({
Key? key,
this.avatarPath,
required this.ownerName,
required this.imagePaths,
}) : super(key: key);
final String? avatarPath;
final String ownerName;
final List<String> imagePaths;
#override
Widget build(BuildContext context) {
if (imagePaths.length == 0) return Text("no image?");
return Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Color(0xFFF2F2F2),
offset: Offset(0.0, 1.0), //(x,y)
blurRadius: 6.0,
),
],
// color: Color(0xFFF2F2F2),
color: Colors.deepOrange,
borderRadius: BorderRadius.all(Radius.circular(10)),
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(4.0),
child: Row(
children: [
CircleAvatar(
child: avatarPath == null ? Icon(Icons.person) : null,
backgroundImage:
avatarPath == null ? null : AssetImage(avatarPath ?? ""),
),
SizedBox(width: 10),
Text(ownerName),
Expanded(
child: SizedBox(height: 30),
),
Icon(Icons.settings),
],
),
),
Carousel(
indicatorBarColor: Colors.transparent,
autoScrollDuration: Duration(seconds: 2),
animationPageDuration: Duration(milliseconds: 500),
activateIndicatorColor: Colors.grey.shade200,
animationPageCurve: Curves.bounceInOut,
indicatorBarHeight: 30,
indicatorHeight: 10,
indicatorWidth: 20,
unActivatedIndicatorColor: Colors.grey.shade700,
stopAtEnd: true,
autoScroll: false,
// widgets
items: imagePaths
.map(
(imagePath) => Container(
decoration: BoxDecoration(color: Colors.black),
child: ConstrainedBox(
constraints: new BoxConstraints(
minHeight: 100,
minWidth: 100,
// maxHeight: 500,
maxHeight: MediaQuery.of(context).size.height * 3 / 2,
maxWidth: MediaQuery.of(context).size.width,
),
child: FittedBox(
fit: BoxFit.scaleDown,
alignment: Alignment.center,
child: Image(
image: AssetImage("$imagePath"),
),
),
),
),
)
.toList(),
),
Padding(
padding: const EdgeInsets.only(
left: 16,
right: 16,
top: 4,
),
child: Row(
children: [
TextButton(
onPressed: () {
print(123);
},
child: Icon(
Icons.star_border_outlined,
size: 30,
// color: Colors.grey,
),
),
Expanded(
child: SizedBox(
height: 30,
),
),
Icon(
Icons.send_outlined,
size: 30,
// color: Colors.grey,
),
],
),
),
],
),
);
}
}
I solved the bug by giving certain height and width values to the Widget that contains images. Using ConstrainedBox and FittedBox means it will load images first and then measure all items' height and widths and then give height value to ListView. So, that's why the app freezes, measuring ListView's size takes a lot of time.
I changed this:
Container(
decoration: BoxDecoration(color: Colors.black),
child: ConstrainedBox(
constraints: new BoxConstraints(
minHeight: 100,
minWidth: 100,
maxHeight: MediaQuery.of(context).size.height * 3 / 2,
maxWidth: MediaQuery.of(context).size.width,
),
child: FittedBox(
fit: BoxFit.scaleDown,
alignment: Alignment.center,
child: Image(
image: AssetImage("$imagePath"),
),
),
),
),
To this:
AspectRatio(
aspectRatio: 1 / 1,
child: Container(
decoration: BoxDecoration(color: Colors.black),
width: MediaQuery.of(context).size.width,
alignment: Alignment.center,
child: ClipRRect(
// borderRadius: BorderRadius.circular(10),
child: Image(
image: AssetImage("${imagePaths[0]}"),
),
),
),
),
I am trying to make a animation show bookmark. Just like the example on this website.The orignial example is too complicated. So I put part of code from the example of flutter doc. To learn the easiest animation. I have put same tag in both hero,but still error.
Please help me out.Thanks!
Sorry my english is poor.
Center(
child: FutureBuilder(
future: fetchData('querySdiList'),
builder: (context, qS) {
if (qS.hasData) {
return CarouselSlider.builder(
itemCount: qS.data.length,
itemBuilder: (BuildContext context, int itemIndex, int i) {
final list = qS.data[itemIndex];
sdiId = list['sdiId'];
print('carousel:' + sdiId.toString());
return Center(
child: SizedBox(
width: 300,
child: Hero(
tag: sdiId.toString(),
child: Material(
child: InkWell(
onTap: () {
// Navigator.pushNamed(context, '/BookMarkPage');
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) {
print('Hero:' + sdiId.toString());
return Container(
// Set background to blue to emphasize that it's a new route.
color: Colors.lightBlueAccent,
padding: const EdgeInsets.all(16.0),
alignment: Alignment.topLeft,
child: SizedBox(
width: 100,
child: Hero(
tag: sdiId.toString(),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () {},
child: Image.network(
_image[0],
fit: BoxFit.contain,
),
),
),
),
));
}));
},
child: Container(
height:
MediaQuery.of(context).size.height / 2,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
offset: Offset(0, 10),
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 10,
)
],
),
margin: EdgeInsets.all(5.0),
child: Stack(
alignment: Alignment.bottomLeft,
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.all(
Radius.circular(15)),
child: Container(
height: MediaQuery.of(context)
.size
.height /
2,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(_image[
Random().nextInt(
_image.length)])),
),
),
),
Container(
height: MediaQuery.of(context)
.size
.height,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(15)),
color: Colors.white,
gradient: LinearGradient(
begin: FractionalOffset.topCenter,
end: FractionalOffset.bottomLeft,
colors: [
Colors.transparent,
Colors.black,
],
stops: [0.0, .85],
),
),
),
Container(
padding: EdgeInsets.all(10),
child: RichText(
text: TextSpan(
text: '${list['sdiName']}',
style: TextStyle(
fontSize: 50,
color: Colors.white),
),
),
height: MediaQuery.of(context)
.size
.height /
8,
),
],
),
),
),
))));
},
options: CarouselOptions(
aspectRatio: 2,
autoPlay: true,
enlargeCenterPage: true,
height: MediaQuery.of(context).size.height,
),
);
} else if (qS.hasError) {
return qS.error;
}
return CircularProgressIndicator();
},
),
),
error
There are multiple heroes that share the same tag within a subtree.
You are creating multiple widgets with the carouselBuilder and each one of them is getting the same tag, you cant do that, the tag is what tells the Hero widget where it should animate to.
The problem seem to be here
final list = qS.data[itemIndex];
sdiId = list['sdiId'];
try setting the hero tag as something like this
tag: qS.data[itemIndex]['sdiId']
this should give each of the heros the sdiId of the itemIndex preventing them from having the same one
I'm trying to add animation to letters as you can see below. The problem I'm facing is that, when I change the size of the selected letter, other elements are moving. I could not find a way to keep the position of the other letters. Any ideas?
return Container(
color: Colors.orange,
padding: EdgeInsets.fromLTRB(5, 0, 5, 0),
child: RowSuper(
alignment: Alignment.center,
innerDistance: -20,
children: [
nodes[0],
ColumnSuper(
alignment: Alignment.center,
innerDistance: -5,
children: [
nodes[1],
nodes[2],
],
),
nodes[3],
]));
return TweenAnimationBuilder(
tween: Tween<double>(begin: 0, end: 1),
duration: Duration(milliseconds: 2000),
builder: (BuildContext context, double _val, Widget child) {
return Opacity(
opacity: _val,
child: HiveNode(
index: index,
child: Container(
height: _val * size,
width: _val * size,
decoration: ShapeDecoration(
color: nodeColor,
shape: PolygonBorder(
sides: 6,
rotate: 90.0,
borderRadius: 8.0,
border: BorderSide(color: Colors.grey, width: 0.1)),
shadows: [
BoxShadow(
color: Colors.grey.withOpacity(0.6),
spreadRadius: 0,
blurRadius: 7,
offset: Offset(-3, 6), // changes position of shadow
),
],
),
child: Center(
child: Text(char,
style: TextStyle(
fontSize: 34,
fontWeight: FontWeight.bold,
color: textColor)),
))),
);
});
I could solve with Stack approach. Here is the answer if anybody needs something similar in future.
return Container(
color: Colors.orange,
width: 360,
height: 400,
child: Stack(
alignment: Alignment.center,
children: [
Positioned(left:0, top:80, child: nodes[0]),
Positioned(left:0, bottom:80, child: nodes[1]),
Positioned(top:20, child: nodes[2]),
Positioned(left:120, top:140, child: nodes[3]),
Positioned(bottom:20, child: nodes[4]),
Positioned(right:0, top:80, child: nodes[5]),
Positioned(right:0, bottom:80, child: nodes[6]),
],
)
);