How to stack the items inside a "Listview.builder" on top of each other? flutter - flutter

I want to stack the items inside a Listview.builder but when I try to do so with the Align() class and set its heightfactor: , it gives padding to the top and bottom and I don't want that, does anyone have any idea how do I stack them properly?
My center SyncfusionLinearGauge:
//Center SyncfusionLinearGauge
Expanded(
child: ScrollablePositionedList.builder(
itemScrollController: itemController,
itemCount: _provider.loadedContestants.length,
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (context, index) {
final data = _provider.loadedContestants[index];
return Align(
heightFactor: 0.0001,
child: SfLinearGauge(
// showLabels: false,
// showTicks: false,
// showAxisTrack: false,
axisLabelStyle: TextStyle(color: Colors.white),
majorTickStyle: LinearTickStyle(
color: Colors.white, length: 10),
minorTickStyle:
LinearTickStyle(color: Colors.white),
axisTrackStyle:
LinearAxisTrackStyle(color: Colors.red),
orientation: LinearGaugeOrientation.vertical,
minimum: 0,
maximum: 100,
animationDuration: 500,
markerPointers: [
//User pointer
LinearWidgetPointer(
value: data.points,
position: LinearElementPosition.cross,
offset: 50.0,
animationDuration: 2000,
child: UserCardWidget(
data: data,
),
),
],
),
);
},
),
),
My user card:
class UserCardWidget extends StatelessWidget {
UserCardWidget({Key key, this.data}) : super(key: key);
DemoUserModel data;
#override
Widget build(BuildContext context) {
bool _isKevinTeam = data.team == 'Kevin';
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(left: 167.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
// mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 15,
height: 15,
decoration: BoxDecoration(
color: const Color(0xff2da162),
borderRadius: BorderRadius.all(
Radius.circular(50),
),
),
),
Card(
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Text(
'${data.firstName.toString()}M',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w700,
fontFamily: "SFProText",
fontStyle: FontStyle.normal,
fontSize: 13.0,
),
),
),
),
],
),
),
);
}
}
What I want:
What I have:

What I ended up doing was removing the listview.builder all together and mapped the list in the markerPointers:[], so this is the result.
My LinearGauge:
Expanded(
flex: 4,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: SizedBox(
height: size.height * 0.57,
child: SfLinearGauge(
showLabels: false,
showTicks: false,
showAxisTrack: false,
orientation: LinearGaugeOrientation.vertical,
minimum: 0,
maximum: 100,
animationDuration: 500,
markerPointers: _sortedList
.asMap()
.map(
(i, data) {
return MapEntry(
i++,
LinearWidgetPointer(
value: data.points,
position: LinearElementPosition.cross,
animationDuration: 2000,
offset: 500,
child: UserCardWidget(
data: data,
index: i,
isExpandedMethod: _isExpanded,
),
),
);
},
)
.values
.toList(),
),
),
),
],
),
),
This is the result:

Related

A widget in my SliverAppBar is causing a bottom overflow, how do i correct this in flutter?

I use a SliverAppBar and use flexibleSpace title instead of the default sliver title, on portrait mode, it is perfectly fine as shown :
But when i get to landscape mode it causes a bottom overflow by 13px, VScode tells me the renderflex is caused by a column.
This is how it looks like in landscape :
It is so messy that when i discovered this bug i couldn't continue coding until i fix this and this is what i've been trying to do :(
I will give my SliverAppBar code and also the widget used in the sliverapp flexibleSpace title as snippet below
I have tried using Expanded instead of Flexible, but it causes even more errors.
I also tried using some screen utility packages in pub.dev but seem like i don't use it properly.
Main view with sliverapp :
class HomeView extends GetView<HomeController> {
#override
Widget build(BuildContext context) {
controller.initScrollController();
return WillPopScope(
onWillPop: Helper().onWillPop,
child: Scaffold(
body: RefreshIndicator(
onRefresh: () async {
Get.find<LaravelApiClient>().forceRefresh();
controller.refreshHome(showMessage: true);
Get.find<LaravelApiClient>().unForceRefresh();
},
child: CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(),
controller: controller.scrollController,
shrinkWrap: false,
slivers: <Widget>[
SliverAppBar(
backgroundColor: Color(0xffFFFFFF),
expandedHeight: MediaQuery.of(context).size.height * 0.18,
elevation: 0.5,
floating: false,
iconTheme: IconThemeData(color: Get.theme.primaryColor),
actions: [NotificationsButtonWidget()],
flexibleSpace: FlexibleSpaceBar(
collapseMode: CollapseMode.parallax,
title: MainProfileDetails(),//i suspect this is the widget causing the bug
),
),
SliverToBoxAdapter(
child: Wrap(
children: [
JobSummaryView(),
//BookingsListWidget(),
],
),
),
],
)),
),
);
}
}
MainProfileDetails() code:
class MainProfileDetails extends StatelessWidget {
const MainProfileDetails({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Obx(() {
return Padding(
padding: const EdgeInsets.only(left: 5.0),
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Stack(
children: [
GestureDetector(
onTap: () {
Get.toNamed(Routes.PROFILE);
},
child: Container(
child: Stack(
children: [
SizedBox(
width: 60,
height: 60,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(80)),
child: CachedNetworkImage(
height: 100,
width: double.infinity,
fit: BoxFit.cover,
imageUrl: Get.find<AuthService>()
.user
.value
.avatar
.thumb,
placeholder: (context, url) => Image.asset(
'assets/img/loading.gif',
fit: BoxFit.cover,
width: double.infinity,
height: 80,
),
errorWidget: (context, url, error) =>
Icon(Icons.error_outline),
),
),
),
Positioned(
top: 35,
left: 30,
right: 0,
child: Get.find<AuthService>()
.user
.value
.verifiedPhone ??
false
? Icon(Icons.check_circle,
color: Color(0xffB0BEC1), size: 24)
: Icon(Icons.error_outline),
)
],
),
),
),
],
),
SizedBox(
width: 10,
),
Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Flexible(
flex: 2,
child: Padding(
padding: const EdgeInsets.only(left: 1.0),
child: Text(
'Hello, ${Get.find<AuthService>().user.value.name}',
style: GoogleFonts.poppins(
color: Color(0xff34495E), fontSize: 9),
),
),
),
Flexible(
flex: 2,
child: Padding(
padding: const EdgeInsets.only(top: 1.0, bottom: 1.0),
child: Text(
'Good Stitching',
style: GoogleFonts.poppins(
fontSize: MediaQuery.of(context).size.width * 0.04,
color: Color(0xff000000),
fontWeight: FontWeight.w600),
),
),
),
Flexible(
child: Container(
decoration: BoxDecoration(
color: Color(0xffeeeeee),
borderRadius: BorderRadius.circular(15),
),
child: Padding(
padding: const EdgeInsets.only(
top: 3.0, bottom: 3.0, left: 10.0, right: 10.0),
child: Get.find<AuthService>().user.value.verifiedPhone ??
false
? Text(
'Verified',
style: GoogleFonts.poppins(
fontSize:
MediaQuery.of(context).size.width * 0.025,
fontStyle: FontStyle.italic),
)
: Text(
'Unverified',
style: GoogleFonts.poppins(
fontSize:
MediaQuery.of(context).size.width * 0.025,
fontStyle: FontStyle.italic),
),
),
)),
],
),
//NotificationsButtonWidget(),
],
),
);
});
}
}
Please i need your time and assistance on this one. Thank you!
After many hours of asking for help here, i decided to go with simple AppBar in flutter as SliverAppBar flexibleSpace title is only customisable to a limit.
And that was goodbye to orientation issues.
Thanks everyone for your support.

Flutter - Managing CarouselSlider to match the width of it's parent and making right and left edges transparent

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(),
);
}
...

Flutter: RenderFlex children have non-zero flex but incoming height constraints are unbounded. Using Expandable listview

I want to design this layout:
I am using a expandable listview inside a column. I was add ListView.builder inside Expanded but still problem continue.
What is the wrong?
This is my code:
Parent page:
class LearningCoursePage extends StatefulWidget {
String courseId;
LearningCoursePage({Key? key, required this.courseId}) : super(key: key);
#override
State<LearningCoursePage> createState() => _LearningCoursePageState();
}
class _LearningCoursePageState extends State<LearningCoursePage> {
CourseModel? courseModel;
Future<void> _loadResource() async {
courseModel = await Get.find<CourseController>().getCourseById(widget.courseId);
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _loadResource(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
backgroundColor: AppColors.yellowColor,
title: courseModel != null ? Text(courseModel!.name.toString()) : Text("Loading..."),
leading: IconButton(
icon: Icon(Icons.close),
onPressed: () {},
),
),
body: GetBuilder<AuthController>(builder: (authController) {
return authController.userLoggedIn() ? Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(Dimensions.radius20),
boxShadow: [
BoxShadow(
color: Color(0xFFe8e8e8),
blurRadius: 5.0,
offset: Offset(0, 5)
),
BoxShadow(
color: Colors.white,
offset: Offset(-5, 0)
),
BoxShadow(
color: Colors.white,
offset: Offset(5, 0)
),
]
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height: 40,),
BigText(text: "Vui lòng đăng nhập để xác minh tài khoản"),
SizedBox(height: 20,),
SizedBox(height: 20,),
GestureDetector(
onTap: (){
Get.toNamed(RouteHelper.getSignInPage());
},
child: Container(
width: Dimensions.screenWidth/2,
height: Dimensions.screenHeight/13,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(Dimensions.radius15),
color: AppColors.yellowColor
),
child: Center(
child: BigText(
text: "ĐĂNG NHẬP",
size: Dimensions.font20+Dimensions.font20/2,
color: Colors.white,
),
),
),
),
SizedBox(height: 40,),
],
),
),
) :
SingleChildScrollView(
child: Container(
child: Column(
children: [
Container(
height: 400,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: AssetImage("assets/image/test.jpg")
)
),
),
Container(
width: double.infinity,
color: AppColors.mainBlackColor,
child: Container(
margin: EdgeInsets.only(left: Dimensions.width20, right: Dimensions.width20),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 30,),
BigText(text: (courseModel != null ? courseModel!.name.toString().toUpperCase() : "Loading..."), size: 20, color: AppColors.whiteColor,),
SizedBox(height: 30,),
BigText(text: "Bạn hoàn thành 3 trong 59 bài giảng", size: 16, color: AppColors.whiteColor,),
SizedBox(height: 30,),
LinearPercentIndicator(
animation: true,
animationDuration: 1000,
lineHeight: 40.0,
backgroundColor: Colors.white,
percent: 0.2,
padding: EdgeInsets.only(right: 0),
center: Text("20.0%"),
trailing: Container(
color: AppColors.yellowColor,
padding: EdgeInsets.only(left: 10, right: 10),
alignment: Alignment.center,
height: 40,
child: SmallText(text: "hoàn thành", size: 16, color: AppColors.whiteColor,),
),
linearStrokeCap: LinearStrokeCap.butt,
progressColor: AppColors.yellowColor,
),
SizedBox(height: 30,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SelectButtonWidget(text: 'Vào học ngay', isSelect: true, width: Dimensions.screenWidth/2.5,),
SelectButtonWidget(text: 'Hướng dẫn học', width: Dimensions.screenWidth/2.5,),
],
),
SizedBox(height: 30,),
SelectButtonWidget(text: 'Xem mẫu chứng chỉ hoàn thành',),
SizedBox(height: 60,),
],
),
)
),
Container(
width: double.infinity,
color: AppColors.greyColor,
child: Container(
margin: EdgeInsets.only(left: Dimensions.width20, right: Dimensions.width20),
child: Column(
children: [
SizedBox(height: 30,),
LearningCourseBody(courseModel: courseModel,)
],
),
),
)
],
),
),
);
}),
);
},
);
}
}
The body page (cluster layout with gray background color):
class LearningCourseBody extends StatefulWidget {
CourseModel? courseModel;
LearningCourseBody({Key? key, this.courseModel}) : super(key: key);
#override
State<LearningCourseBody> createState() => _LearningCourseBodyState();
}
class _LearningCourseBodyState extends State<LearningCourseBody> {
#override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SelectButtonWidget(text: 'Tổng quan', isSelect: true, width: Dimensions.screenWidth/3.5,),
SelectButtonWidget(text: 'Bài học', textColor: AppColors.mainBlackColor, width: Dimensions.screenWidth/3.5,),
SelectButtonWidget(text: 'Tài liệu', textColor: AppColors.mainBlackColor, width: Dimensions.screenWidth/3.5,),
],
),
SizedBox(height: 30,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: 5,),
SelectButtonWidget(text: 'Hỏi & đáp', textColor: AppColors.mainBlackColor, width: Dimensions.screenWidth/3.5,),
SizedBox(width: 5,),
SelectButtonWidget(text: 'Ghi chép', textColor: AppColors.mainBlackColor, width: Dimensions.screenWidth/3.5,),
SizedBox(width: 5,),
],
),
SizedBox(height: 60,),
// OverviewCourse(courseModel: widget.courseModel, rating: 5,)
(widget.courseModel != null && widget.courseModel!.particalsCourse != null) ? Expanded(child: ExpandableListViewWidget(courseModel: widget.courseModel!,)) : Text('Loading....'),
],
);
}
}
And this is the expandable lisview:
class ExpandableListViewWidget extends StatefulWidget {
CourseModel courseModel;
ExpandableListViewWidget({Key? key, required this.courseModel}) : super(key: key);
#override
State<ExpandableListViewWidget> createState() => _ExpandableListViewWidgetState();
}
class _ExpandableListViewWidgetState extends State<ExpandableListViewWidget> {
List<Widget> _buildExpandList(int index) {
List<Widget> res = [];
if (widget.courseModel.particalsCourse![index].lessons == null) return res;
for (var sub in widget.courseModel.particalsCourse![index].lessons!) {
var s = ListTile(
title: Text(sub.title!),
leading: Icon(Icons.play_circle),
trailing: sub.isTrial! ? SmallText(text: 'học thử', color: AppColors.yellowColor,) : Text(sub.timeLearning!.toString()),
);
res.add(s);
}
return res;
}
#override
Widget build(BuildContext context) {
List<bool> tileExpanded = List<bool>.generate(widget.courseModel.particalsCourse!.length, (index) => false);
return ListView.builder(
itemCount: widget.courseModel.particalsCourse!.length,
itemBuilder: (context, index) {
return Container(
child: ExpansionTile(
title: Text(widget.courseModel.particalsCourse![index].title!),
controlAffinity: ListTileControlAffinity.leading,
leading: Icon(
tileExpanded[index] ? Icons.arrow_drop_up : Icons.arrow_drop_down
),
children: _buildExpandList(index),
onExpansionChanged: (bool expanded) {
setState(() => tileExpanded[index] = expanded);
},
),
);
}
);
}
}
Hope your help? Thanks!!!
Finally, I was resolve this problem. This is my step:
Step 1: Column use mainAxisSize: MainAxisSize.min
Step 2: Change Expanded to Flexible
Step 3: Adding this two lines in ExpandableListViewWidget
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
...

How to navigate with the side menu with just one screen

I have a demand to perform the navigation of this left side menu. This screen is already divided into 3, but I need the side menu only to update the second (middle). And I need the third one to receive the index data from the second (middle).
main_scren.dart
class MainScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
// It provide us the width and height
Size _size = MediaQuery.of(context).size;
return Scaffold(
body: Responsive(
// Let's work on our mobile part
mobile: ListOfChannels(),
tablet: Row(
children: [
Expanded(
flex: 6,
child: ListOfChannels(),
),
Expanded(
flex: 9,
child: ChannelScreen(),
),
],
),
desktop: Row(
children: [
// Once our width is less then 1300 then it start showing errors
// Now there is no error if our width is less then 1340
Expanded(
flex: _size.width > 1340 ? 2 : 4,
child: SideMenu(),
),
Expanded(
flex: _size.width > 1340 ? 3 : 5,
child: ListOfChannels(),
),
Expanded(
flex: _size.width > 1340 ? 8 : 10,
child: ChannelScreen(),
),
],
),
),
);
}
}
Here is the code for my side_menu.dart
#override
Widget build(BuildContext context) {
return Container(
height: double.infinity,
padding: EdgeInsets.only(top: kIsWeb ? kDefaultPadding : 0),
color: kBgLightColor,
child: SafeArea(
child: SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: kDefaultPadding),
child: Column(
children: [
Row(
children: [
Spacer(),
Image.asset(
"assets/images/logo_lado.png",
width: 100,
),
Spacer(),
// We don't want to show this close button on Desktop mood
if (!Responsive.isDesktop(context)) CloseButton(),
],
),
SizedBox(height: kDefaultPadding),
SizedBox(height: kDefaultPadding),
CircleAvatar(
maxRadius: 65,
backgroundColor: Colors.transparent,
backgroundImage: AssetImage("assets/images/user_3.png"),
),
SizedBox(height: kDefaultPadding),
SizedBox(height: kDefaultPadding),
FlatButton.icon(
minWidth: double.infinity,
padding: EdgeInsets.symmetric(
vertical: kDefaultPadding,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
color: kPrimaryColor,
onPressed: () {},
icon: WebsafeSvg.asset("assets/Icons/Edit.svg", width: 16),
label: Text(
"Meu Perfil",
style: TextStyle(color: Colors.white),
),
).addNeumorphism(
topShadowColor: Colors.white,
bottomShadowColor: Color(0xFF234395).withOpacity(0.2),
),
SizedBox(height: kDefaultPadding),
FlatButton.icon(
minWidth: double.infinity,
padding: EdgeInsets.symmetric(
vertical: kDefaultPadding,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
color: kBgDarkColor,
onPressed: () async {
bool saiu = await logout();
if (saiu) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => LoginPage())
);
}
},
icon: WebsafeSvg.asset("assets/Icons/Download.svg", width: 16),
label: Text(
"Sair",
style: TextStyle(color: kTextColor),
),
).addNeumorphism(),
SizedBox(height: kDefaultPadding * 2),
// Menu Items
SideMenuItem(
press: () {
},
title: "On Demand",
iconSrc: "assets/Icons/new_releases_black_24dp.svg",
isActive: false,
//itemCount: 3,
),
SideMenuItem(
press: () {},
title: "Assistir TV",
iconSrc: "assets/Icons/tv_black_24dp.svg",
isActive: true,
),
SideMenuItem(
press: () {},
title: "Favoritos",
iconSrc: "assets/Icons/star_border_black_24dp.svg",
isActive: false,
),
//SizedBox(height: kDefaultPadding * 2),
// Tags
//Tags(),
],
),
),
),
);
And this is the screen that is in error, from which I need to receive the channel list data.
channel_screen.dart
class ChannelScreen extends StatelessWidget {
const ChannelScreen({
Key key,
this.channel,
}) : super(key: key);
final Channel channel;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.white,
child: SafeArea(
child: Column(
children: [
//Header(),
//Divider(thickness: 1),
Expanded(
child: SingleChildScrollView(
padding: EdgeInsets.all(kDefaultPadding),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CircleAvatar(
maxRadius: 24,
backgroundColor: Colors.transparent,
backgroundImage: NetworkImage(channel.midiaImagemUrl),
),
SizedBox(width: kDefaultPadding),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text.rich(
TextSpan(
text: channel.midiaTitulo,
style: Theme.of(context)
.textTheme
.button,
children: [
TextSpan(
text:
"",
style: Theme.of(context)
.textTheme
.caption),
],
),
),
Text(
channel.midiaTitulo,
style: Theme.of(context)
.textTheme
.headline6,
)
],
),
),
SizedBox(width: kDefaultPadding / 2),
Text(
"10:30 - 12:00",
style: Theme.of(context).textTheme.caption,
),
],
),
SizedBox(height: kDefaultPadding),
LayoutBuilder(
builder: (context, constraints) => SizedBox(
width: constraints.maxWidth > 850
? 800
: constraints.maxWidth,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Aqui ficará o EPG List",
style: TextStyle(
height: 1.5,
color: Color(0xFF4D5875),
fontWeight: FontWeight.w300,
),
),
SizedBox(height: kDefaultPadding),
Divider(thickness: 1),
SizedBox(height: kDefaultPadding / 2),
SizedBox(
height: 500,
width: 500,
child: StaggeredGridView.countBuilder(
physics: NeverScrollableScrollPhysics(),
crossAxisCount: 1,
itemCount: 1,
itemBuilder:
(BuildContext context, int index) =>
ClipRRect(
borderRadius:
BorderRadius.circular(8),
child: Image.asset(
"assets/images/Img_$index.png",
fit: BoxFit.cover,
),
),
staggeredTileBuilder: (int index) =>
StaggeredTile.count(
2,
index.isOdd ? 2 : 1,
),
mainAxisSpacing: kDefaultPadding,
crossAxisSpacing: kDefaultPadding,
),
)
],
),
),
),
],
),
),
],
),
),
)
],
),
),
),
);
}
}
Any help will be appreciated.
The basic problem here is that ChannelScreen has a final channel property, but the property isn't passed into the constructor - so it's always null when you try to access it.
The most straightforward solution is to convert your MainScreen to a StatefulWidget, with a mutable channel property. Then add a function onChannelSelected to ListOfChannels to handle when a user selects a new channel from that view - You could then update the state from that handler.
For a bare minimum example:
class ListOfChannels extends StatelessWidget {
final void Function(Channel) onChannelSelected;
ListOfChannels({required this.onChannelSelected});
#override
Widget build(BuildContext context) {
return Column(
children: channels.map((channel) => FlatButton(
onPressed: () { this.onChannelSelected(channel) },
))).toList(),
);
}
}
class _MainScreenState extends State<MainScreen> {
Channel channel; // Initialize to an appropriate value, or handle null case
#override
Widget build(BuildContext context) {
// Desktop section
Row(
children: [
// Once our width is less then 1300 then it start showing errors
// Now there is no error if our width is less then 1340
Expanded(
flex: _size.width > 1340 ? 2 : 4,
child: SideMenu(),
),
Expanded(
flex: _size.width > 1340 ? 3 : 5,
child: ListOfChannels(
onChannelSelected: (channel) {
setState(() { this.channel = channel; });
}
),
),
Expanded(
flex: _size.width > 1340 ? 8 : 10,
child: ChannelScreen(channel: channel),
),
],
),
}
}

State of each widget in ListView.builder is affecting each other. How to differentiate widgets in ListView.builder?

I am building an app that has gmail type animation in which when the circular avatar button is clicked the List tile gets selected with a rotation animation.
The list of items are built using ListView.builder. But when the circle avatar of a list item is clicked it affects the whole list of items and rotates every circle avatar. From similar questions asked I changed Listtile into a separate stateful widget as every item could hold its own state. But this doesn't change the output.
I didn't get the output even after using keys(Unique and object). I am stuck here for a long time but couldn't figure what and how to do it. Is there any way to differentiate widgets in Listview.builder and maintain their states seperately?
Alternatively when I use checkbox/selectable icon its holding the state for each item induvidually.
class MailList extends StatefulWidget {
int index;
List<MailModel> account;
Animation rotationAnim;
AnimationController circleAvatarController;
OneShotAnimation riveAnimationController1;
OneShotAnimation riveAnimationController2;
List<int> selectedIndexes = [];
UniqueKey key;
MailList({
required this.index,
required this.account,
required this.rotationAnim,
required this.circleAvatarController,
required this.riveAnimationController1,
required this.riveAnimationController2,
required this.selectedIndexes,
required this.key
}):super(key: key);
#override
_MailListState createState() => _MailListState();
}
class _MailListState extends State<MailList> {
#override
Widget build(BuildContext context) {
return Listener(
key: UniqueKey(),
child: Dismissible(
key: ValueKey(widget.index),
child: GestureDetector(
key: ValueKey(widget.index),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
shape: BoxShape.rectangle,
color: widget.account[widget.index].isSelected ? Colors.blue.withOpacity(0.2): null
),
height: 70,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
GestureDetector(
child: AnimatedBuilder(
builder: (context, child){
return Transform(
alignment: Alignment.center,
child: CircleAvatar(
key: ValueKey(widget.index),
backgroundColor: widget.account[widget.index].isSelected ? widget.account[widget.index].defaultOnSelectedColor : widget.account[widget.index].senderInfo.profileColor.withOpacity(0.75),
child: TweenAnimationBuilder(
tween: Tween<double>(begin: 0, end: 1),
builder: (context, double anim, child){
return widget.rotationAnim.value > 90 && widget.account[widget.index].isSelected
? Opacity(
child: Icon(
widget.account[widget.index].defaultOnSelectedIcon,
color: Colors.white,
),
opacity: anim,
)
: Text(
widget.account[widget.index].senderInfo.senderName[0].toString(),
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 21.0,
color: Colors.white
));
},
duration: const Duration(seconds: 1),
),
),
transform: /*isSelected(widget.index) ? (Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY((widget.rotationAnim.value) / 180 * math.pi))
: (Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(0 / 180 * math.pi))*/
Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY((widget.rotationAnim.value) / 180 * math.pi),
);
},
animation: widget.circleAvatarController,
),
onTap: (){
selectUnSelect(widget.index);
widget.account[widget.index].isSelected = !widget.account[widget.index].isSelected;
widget.account[widget.index].isSelected ? widget.circleAvatarController.forward() : widget.circleAvatarController.reverse();
setState(() {
});
},
)
],
mainAxisAlignment: MainAxisAlignment.start,
),
const SizedBox(
width: 10.0,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.account[widget.index].senderInfo.senderName,
style: TextStyle(
fontSize: 16.0,
fontWeight: widget.account[widget.index].isSeen
? FontWeight.normal
: FontWeight.bold
),
),
Text(
"10:00"
)
],
)
),
const SizedBox(
height: 5.0,
),
Text(
widget.account[widget.index].title,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14.5,
fontWeight: widget.account[widget.index].isSeen
? FontWeight.normal
: FontWeight.bold
),
),
const SizedBox(
height: 5.0,
),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: Text(
widget.account[widget.index].content,
overflow: TextOverflow.ellipsis,
),
),
GestureDetector(
child: Container(
child: widget.account[widget.index].starred ? Icon(
Icons.star,
color: Colors.orange[300],
):
Icon(
Icons.star_border
),
),
onTap: (){
setState(() {
widget.account[widget.index].starred = !widget.account[widget.index].starred;
});
},
)
],
),
),
const SizedBox(
height: 5.0,
),
],
),
)
],
),
),
),
onTap: (){
},
),
background: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: dragWidget(
left: 8.0,
controller: widget.riveAnimationController1
)),
],
),
secondaryBackground: Container(
alignment: Alignment.centerRight,
color: Colors.green,
child: Padding(
padding: EdgeInsets.only(
right: 8.0
),
child: SizedBox(
height: 50.0,
width: 50.0,
child: RiveAnimation.asset(
"assets/gmail_drag_anim.riv",
controllers: [
widget.riveAnimationController2
],
animations: [
"Animation 1"
],
fit: BoxFit.fill,
//antialiasing: false,
),
),
),
),
onDismissed: (direction){
widget.account.removeAt(widget.index);
},
),
);
}
bool isSelected(int index){
return widget.selectedIndexes.contains(index);
}
selectUnSelect(int index){
if(isSelected(index)){
widget.circleAvatarController.reverse();
Future.delayed(Duration(milliseconds: 320), (){
widget.selectedIndexes.remove(index);
setState(() {
});
});
}else{
widget.selectedIndexes.add(index);
}
}
Widget dragWidget({
double? left,
double? right,
required OneShotAnimation controller
}){
return Container(
alignment: right != null ? Alignment.centerRight : Alignment.centerLeft,
color: Colors.green,
child: Padding(
padding: EdgeInsets.only(
left: left != null ? left : 0.0,
right: right != null ? right: 0.0
),
child: SizedBox(
height: 50.0,
width: 50.0,
child: RiveAnimation.asset(
"assets/gmail_drag_anim.riv",
controllers: [
controller
],
animations: [
"Animation 1"
],
fit: BoxFit.fill,
//antialiasing: false,
),
),
),
);
}
} ```
// ListView.builder:
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index){
return MailList(
key: GlobalKey<_MailListState>(),
index: index,
account: account1_mail_data,
rotationAnim: rotationAnim,
circleAvatarController: circleAvatarController,
riveAnimationController1: _riveAnimationController1,
riveAnimationController2: _riveAnimationController2,
selectedIndexes: selectedIndexes
);
},
childCount: account1_mail_data.length
),
)