Flutter Infinite Grid View Pagination - flutter

I Have an (infinitepagedgridview) which I constructed using the "infinite_scroll_pagination" Library
everything is running perfectly for me but I'm facing an issue using the pagingcontroller.refresh()
method, which is triggered using a button which changes a variable state which controls a specific parameter that causes different api results in the (infinitepagedgridview), My issue is that whenever I click the button, the pagingcontroller gets refreshed but the first three elements in the gridview which are the first page results come from the last api request before the button got pressed and the rest of the paginated pages work perfectly.
button code:
onTap: () async {
setState(() => FFAppState().homecat = 'Bags');
setState(() => _pagingController?.refresh());
}
listcode and api call:
child: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: RefreshIndicator(
onRefresh: () async {
setState(() => _pagingController?.refresh());
await waitForOnePage();
},
child: PagedGridView<ApiPagingParams, dynamic>(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
mainAxisSpacing: 0.0,
crossAxisSpacing: 5.0,
crossAxisCount: 2,
childAspectRatio: 0.59,
),
pagingController: () {
if (_pagingController != null) {
return _pagingController!;
}
_pagingController = PagingController(
firstPageKey: ApiPagingParams(
nextPageNumber: 0,
numItems: 0,
lastResponse: null,
),
);
_pagingController!
.addPageRequestListener((nextPageMarker) {
GetAllProductsCall.call(
offset: nextPageMarker.numItems,
categoryCatName: FFAppState().homecat)
.then((gridViewinfiniteGetAllProductsResponse) {
List pageItems = getJsonField(
gridViewinfiniteGetAllProductsResponse.jsonBody,
r'''$.results''',
).toList() as List;
// print('22' + pageItems.toString());
final newNumItems =
nextPageMarker.numItems + pageItems.length;
_pagingController!.appendPage(
pageItems,
(pageItems.length > 0)
? ApiPagingParams(
nextPageNumber:
nextPageMarker.nextPageNumber + 1,
numItems: newNumItems,
lastResponse:
gridViewinfiniteGetAllProductsResponse,
)
: null,
);
});
#override
#override
void didUpdateWidget(PagedGridView oldWidget) {
_pagingController?.refresh();
}
});
return _pagingController!;
}(),
padding: EdgeInsets.zero,
scrollDirection: Axis.vertical,
builderDelegate: PagedChildBuilderDelegate<dynamic>(
// Customize what your widget looks like when it's loading the first page.
firstPageProgressIndicatorBuilder: (_) => Center(
child: SizedBox(
width: 50,
height: 50,
child: CircularProgressIndicator(
color: FlutterFlowTheme.of(context).primaryColor,
),
),
),
itemBuilder: (context, _, kokoIndex) {
final kokoItem =
_pagingController!.itemList![kokoIndex];
return InkWell(
onTap: () async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailsWidget(
id: getJsonField(
kokoItem,
r'''$.id''',
).toString(),
),
),
);
},
child: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
color: FlutterFlowTheme.of(context)
.secondaryBackground,
borderRadius: BorderRadius.circular(12),
),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
Expanded(
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: EdgeInsetsDirectional
.fromSTEB(0, 0, 0, 0),
child: ClipRRect(
borderRadius:
BorderRadius.circular(0),
child: Image.network(
getJsonField(
kokoItem,
r'''$.thumbnail_img1''',
),
width: MediaQuery.of(context)
.size
.width,
height: MediaQuery.of(context)
.size
.height *
0.3,
fit: BoxFit.cover,
),
),
),
],
),
),
],
),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: AutoSizeText(
getJsonField(
kokoItem,
r'''$.title''',
).toString(),
textAlign: TextAlign.right,
maxLines: 2,
style: FlutterFlowTheme.of(context)
.bodyText1
.override(
fontFamily: 'Cairo',
fontSize: 13,
useGoogleFonts: GoogleFonts
.asMap()
.containsKey(
FlutterFlowTheme.of(
context)
.bodyText1Family),
),
),
),
ToggleIcon(
onPressed: () async {
setState(() =>
FFAppState().smallsize =
!FFAppState().smallsize);
},
value: FFAppState().smallsize,
onIcon: Icon(
Icons.favorite,
color: Colors.black,
size: 25,
),
offIcon: Icon(
Icons.favorite_border,
color: Colors.black,
size: 25,
),
)
],
),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
functions.priceformat(getJsonField(
kokoItem,
r'''$.price''',
)),
style: FlutterFlowTheme.of(context)
.bodyText1,
),
Icon(
FFIcons.kshekel,
color: Color(0xD8000000),
size: 13,
),
],
),
],
),
),
);
},
),
),
),
),
),
],
),
).animateOnActionTrigger(
animationsMap['columnOnActionTriggerAnimation']!,
hasBeenTriggered: hasColumnTriggered),
),
);
}
I tried loading using future or clearing the paging controller before refreshing neither worked,
any help would be much appreciated :)

Related

How to avoid RenderFlow error when keyboard appears in Flutter

I am designing a screen with two main sections:
A carousel slider with a TextField below it
A button
I want the button to be at the bottom of the screen.
To achieve this, I have wrapped the column for 1. in a Flexible widget and set the mainAxisSize for the Column to max, and the mainAxis size for the Column containing the button to min.
Now when I click on the TextField, the keyboard appears, and I receive a Bottom Overflowed error with the Button appearing on top of the TextField.
How do I ensure that the Button stays at the bottom of the screen when the keyboard appears? I have tried wrapping both the Columns in another Column which in turn had been wrapped by a SingleChildScrollView widget, but that overrides the MainAxisSize.max property apparently, and renders the lower Column (containing the Button) just below the upper Column as seen below.
My Code:
class SigninScreen extends StatefulWidget {
const SigninScreen({super.key});
#override
State<SigninScreen> createState() => _SigninScreenState();
}
class _SigninScreenState extends State<SigninScreen> {
int _currentCarouselIndex = 0;
List<Widget> indicators(imagesLength, currentIndex) {
return List<Widget>.generate(imagesLength, (index) {
return Container(
margin: EdgeInsets.symmetric(
vertical: 1.h,
horizontal: 2,
),
width: currentIndex == index ? 15 : 10,
height: 3,
decoration: BoxDecoration(
color: currentIndex == index ? primary : grey,
borderRadius: const BorderRadius.all(
Radius.circular(2),
),
),
);
});
}
TextEditingController phoneController = TextEditingController();
#override
void dispose() {
phoneController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: GestureDetector(
onTap: (){ FocusManager.instance.primaryFocus?.unfocus();},
behavior: HitTestBehavior.opaque,
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
fit: FlexFit.loose,
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
CarouselSlider(
items: carouselImageList.map<Widget>((i){
return Builder(
builder: (context){
return Container(
width: 100.w,
height: 83.w,
decoration: BoxDecoration(
image: DecorationImage(image: AssetImage(i), fit: BoxFit.fill),
),
);
}
);
}).toList(),
options: CarouselOptions(
height: 83.w,
aspectRatio: 1/0.83,
autoPlay: true,
autoPlayInterval: const Duration(seconds: 3),
initialPage: 0,
viewportFraction: 1,
onPageChanged: (index, timed) {
setState(() {
_currentCarouselIndex = index;
});
}
),
),
SizedBox(height: 1.h,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: indicators(
carouselImageList.length, _currentCarouselIndex),
),
SizedBox(height: 2.h,),
Padding(
padding: EdgeInsets.symmetric(horizontal: 5.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(isEnglish ? "Enter your phone number" : "अपना फ़ोन नंबर दर्ज करें", style: globalTextStyle.copyWith(fontSize: 5.w, fontWeight: FontWeight.bold),),
],
),
SizedBox(height: 1.h),
PhoneNumberField(phoneController: phoneController),
SizedBox(height: 1.h),
Text(isEnglish ? "OTP will be sent on this number." : "इस नंबर पर ओटीपी भेजा जाएगा।", style: globalTextStyle.copyWith(fontSize: 3.w,),),
],
),
),
],
),
),
Column(
mainAxisSize: MainAxisSize.min,
children: [
CustomButton(width: 90.w, height: 15.w, color: primary, onTap: (){
// Navigator.pushNamed(context, otp);
(phoneController.text.length == 10) ?
Navigator.push(context, MaterialPageRoute(builder: (context) =>
OTPScreen(phoneNumber: "+91${phoneController.text}")))
: ShowSnackbar.showSnackBar(context, isEnglish ? "Enter a valid 10 digit phone number." : "एक मान्य 10 अंकों का फ़ोन नंबर दर्ज करें।");
}, text: isEnglish ? "Get OTP" : "ओटीपी प्राप्त करें", fontColor: white, borderColor: primary,),
SizedBox(height: 1.h,),
Text(isEnglish ? "By signing up, you agree to our Terms and Services" : "साइन अप करके, आप हमारी शर्तों से सहमत होते हैं और सेवाएं", style: globalTextStyle.copyWith(fontSize: 2.5.w,),),
SizedBox(height: 2.h,),
],
),
],
),
),
),
),
);
}
}
How do I get the Button to stay at the bottom of the screen even when the keyboard appears?
Remove the Flexible and try the below code.
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: GestureDetector(
onTap: () {
FocusManager.instance.primaryFocus?.unfocus();
},
behavior: HitTestBehavior.opaque,
child: SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Column(
children: [
CarouselSlider(
items: carouselImageList.map<Widget>((i) {
return Builder(builder: (context) {
return Container(
width: 100.w,
height: 100.w,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(i), fit: BoxFit.fill),
),
);
});
}).toList(),
options: CarouselOptions(
height: 83.w,
aspectRatio: 1 / 0.83,
autoPlay: true,
autoPlayInterval: const Duration(seconds: 3),
initialPage: 0,
viewportFraction: 1,
onPageChanged: (index, timed) {
setState(() {
_currentCarouselIndex = index;
});
}),
),
SizedBox(
height: 1.h,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: indicators(
carouselImageList.length, _currentCarouselIndex),
),
SizedBox(
height: 2.h,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 5.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
"Enter your phone number",
style: TextStyle(
fontSize: 5.w, fontWeight: FontWeight.bold),
),
],
),
SizedBox(height: 1.h),
TextFormField(
controller: phoneController,
keyboardType: TextInputType.phone,
),
SizedBox(height: 1.h),
Text(
"OTP will be sent on this number.",
style: TextStyle(
fontSize: 3.w,
),
),
],
),
),
Expanded(
child: Container(),
),
Column(
mainAxisSize: MainAxisSize.min,
children: [
CustomButton(
width: 90.w,
height: 15.w,
color: primary!,
onTap: () {},
text: "Get OTP",
fontColor: white!,
borderColor: primary!,
),
SizedBox(
height: 1.h,
),
Text(
"By signing up, you agree to our Terms and Services",
style: TextStyle(
fontSize: 2.5.w,
),
),
SizedBox(
height: 2.h,
),
],
),
],
),
),
),
),
),
);
}
In your Scaffold set resizeToAvoidBottomInset: property to false I think this will help. Otherwise I observed that the render overflow error is from the column that contains the PhoneNumberField so try Increasing the height of the SizedBox after the PhoneNumberField if the first method doesn't work.

How can I create a multi-level dependent list (dropdown) using CupertinoPicker in flutter?

Based on the serviceAreas list, I want to return the corresponding bus stops. For Instance, If a user selects Oyo in the serviceArea list, the list to return in the second cupertinoPicker should be the ibadanBusstop, and if a user selects Lagos in the first list, the return should be lagosBusstop list.
I'm open to other approaches to make this work but I don't want to use a dropdown.
class _BookingPageState extends State<BookingPage> {
FixedExtentScrollController scrollController = FixedExtentScrollController();
final List<String> ibadanBusstop = [
'Lead City Bus Stop',
'Olorunsogo Bus Stop',
'Challenge Bus Stop',
'Mokola Bus Stop',
];
final List<String> lagosBusstop = [
'Jibowu Bus Stop',
'Obalende Bus Stop',
];
final List<String> serviceAreas = [
'Lagos',
'Oyo',
];
int ibadanIndex = 0;
int lagosIndex = 0;
int serviceAreaIndex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: FraserDimensions.xFlexHeight(context, 24),
),
GestureDetector(
onTap: (() {
scrollController.dispose();
scrollController =
FixedExtentScrollController(initialItem: serviceAreaIndex);
showCupertinoModalPopup(
context: context,
builder: (context) => CupertinoActionSheet(
actions: [
SizedBox(
height: FraserDimensions.xFlexHeight(context, 3),
child: CupertinoPicker(
scrollController: scrollController,
itemExtent: 64,
onSelectedItemChanged: (index) {
setState(() {
this.serviceAreaIndex = index;
});
final item = serviceAreas[index];
print(item);
},
children: serviceAreas
.map((e) => Center(
child: Text(
e,
style: FraserTextStyle.textBlack24,
),
))
.toList()),
)
],
),
);
}),
child: Container(
width: FraserDimensions.xFlexWidth(context, 1),
height: FraserDimensions.xFlexHeight(context, 18),
decoration: BoxDecoration(
color: FraserColors.line,
borderRadius: const BorderRadius.all(
Radius.circular(24),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 24.0),
child: Text(
serviceAreas[serviceAreaIndex],
style: FraserTextStyle.textGrey16,
),
),
),
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Icon(
Icons.arrow_drop_down_outlined,
color: FraserColors.primaryBlack,
size: 22,
),
),
],
),
),
),
SizedBox(
height: FraserDimensions.xFlexHeight(context, 64),
),
GestureDetector(
onTap: (() {
scrollController.dispose();
scrollController =
FixedExtentScrollController(initialItem: ibadanIndex);
showCupertinoModalPopup(
context: context,
builder: (context) => CupertinoActionSheet(
actions: [
SizedBox(
height: FraserDimensions.xFlexHeight(context, 3),
child: CupertinoPicker(
scrollController: scrollController,
itemExtent: 64,
onSelectedItemChanged: (index) {
setState(() {
this.ibadanIndex = index;
});
final item = ibadanBusstop[index];
print(item);
},
children: ibadanBusstop
.map((e) => Center(
child: Text(
e,
style: FraserTextStyle.textBlack24,
),
))
.toList()),
)
],
),
);
}),
child: Container(
width: FraserDimensions.xFlexWidth(context, 1),
height: FraserDimensions.xFlexHeight(context, 18),
decoration: BoxDecoration(
color: FraserColors.line,
borderRadius: const BorderRadius.all(
Radius.circular(24),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 24.0),
child: Text(
ibadanBusstop[ibadanIndex],
style: FraserTextStyle.textGrey16,
),
),
),
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Icon(
Icons.arrow_drop_down_outlined,
color: FraserColors.primaryBlack,
size: 22,
),
),
],
),
),
),
],
),
),
);
}
Because I don't have the BookingPage I can't render to check the result.
But I think you should put some conditions where the ibadanBusstop list and lagosBusstop list is being used. So based on the position of the serviceAreaIndex you make some decision. So say the we are using busStopList Eg. busStopList = serviceAreaIndex == 0 ? ibadanBusstop : lagosBusstop;.
With this the logic of deciding the kind of list is taken care of by the condition.

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),
),
],
),
}
}

flutter AnimatedPositioned cause a Ui jank

I am using AnimatedPositioned to make animation.
Obx(() {
return AnimatedBuilder(
animation: controller.animationController,
builder: (context, child) =>
AnimatedPositioned(
duration: const Duration(milliseconds: 1000),
bottom: controller.actionsBottom,
child: child!,
),
child:
!controller.trloaded.value
? const SizedBox()
: Column(
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
GlobalButton(
title: controller.trLanding('signUp'),
type: ButtonType.outlinedPrimary,
width: 154.w,
height: 56,
onTap: () => controller.goToSignUp(),
),
SizedBox(width: 20.w),
GlobalButton(
borderWidth: 1,
borderColor: AppColors.primary[800]!,
title: controller.trLanding("signIn"),
width: 154.w,
height: 56,
onTap: () => controller.goToSignIn()),
],
),
Container(
margin: const EdgeInsets.only(top: 16, bottom: 16),
child: Row(
children: [
Text(
"By Signing up you agree with our ",
style: textTheme.bodyText2!.copyWith(
color: AppColors.neutral[400],
),
),
GestureDetector(
onTap: () {
// clearData();
push(TermsPage());
},
child: Text(
"Terms and Conditions",
style: textTheme.bodyText2!.copyWith(
color: AppColors.primary[700],
),
),
),
],
),
),
],
),
);
}),
When I run the app by profile mode I got these problems:
To fix that I am using Animation Builder and I added my widgets to its child but I have the above problem. How can I fix that?

Flutter UI List Item

I am using the below code but not able to achieve the desired result, I am new to the flutter world so let me know where to improve to get the desired result. Here is the source code of what I have done.
return Expanded(
child: GridView.builder(
controller: _scrollController,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemCount: albumList.length,
itemBuilder: (BuildContext context, int index) {
return buildRow(index);
},
),
);
Widget buildRow(int index) {
return AlbumTile(
index: index,
albumList: albumList,
deleteAlbum: _deleteAlbum,
);
}
This the Album Tile
class AlbumTile extends StatelessWidget {
AlbumTile(
{Key key,
#required this.index,
#required this.albumList,
#required this.deleteAlbum})
: super(key: key);
final int index;
final List<Album> albumList;
final Function deleteAlbum;
#override
build(BuildContext context) {
String thumb;
if (albumList.elementAt(index).thumbUrl != "") {
thumb = WEBSERVICE_IMAGES +
albumList.elementAt(index).userId.toString() +
'/' +
albumList.elementAt(index).id.toString() +
'/' +
albumList.elementAt(index).thumbUrl;
} else {
thumb = "https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823__340.jpg";
}
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
new AlbumPage(tabIndex: index, albumList: albumList),
),
);
},
child: Container(
// height of the card which contains full item
height: MediaQuery.of(context).size.height * 0.4,
width: MediaQuery.of(context).size.width * 0.28,
// this is the background image code
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
// url is my own public image url
image: NetworkImage(thumb),
),
borderRadius: BorderRadius.circular(12.0)),
// this is the item which is at the bottom
child: Align(
// aligment is required for this
alignment: Alignment.bottomLeft,
// items height should be there, else it will take whole height
// of the parent container
child: Container(
padding: EdgeInsets.only(left: 10.0, right: 0.0),
height: MediaQuery.of(context).size.height * 0.1,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Text() using hard coded text right now
Text(albumList.elementAt(index).name,
style: TextStyle(
fontSize: 18.5,
color: Colors.white,
fontWeight: FontWeight.w500)),
SizedBox(height: 3.0),
Text(albumList.elementAt(index).photos.toString() +' photos',
style: TextStyle(
fontSize: 12.5,
color: Colors.white,
fontWeight: FontWeight.w500))
],
),
),
// pop-up item
PopupMenuButton(
icon: Icon(Icons.more_vert, color: Colors.white),
itemBuilder: (_) => <PopupMenuItem<String>>[
new PopupMenuItem<String>(
child: Row(
children: <Widget>[
Icon(Icons.delete),
Text(
'Delete Album',
),
],
),
value: 'Delete',
),
],
onSelected: (value) {
deleteAlbum(albumList.elementAt(index).id, index);
},
),
],
),
),
),
),
);
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
new AlbumPage(tabIndex: index, albumList: albumList),
),
);
},
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Stack(
fit: StackFit.loose,
children: <Widget>[
ClipRRect(
borderRadius: new BorderRadius.circular(8.0),
child: FadeInImage.assetNetwork(
height: 1000,
placeholder: kPlaceHolderImage,
image: thumb,
fit: BoxFit.cover,
),
),
Align(
alignment: Alignment.bottomLeft,
child: Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 0, 0, 0),
child: Text(
albumList.elementAt(index).name,
style: TextStyle(
color: Colors.white,
fontSize: 18.5,
),
textAlign: TextAlign.left,
),
),
),
PopupMenuButton(
icon: ImageIcon(
AssetImage("graphics/horizontal_dots.png"),
color: Colors.white,
),
itemBuilder: (_) => <PopupMenuItem<String>>[
new PopupMenuItem<String>(
child: Row(
children: <Widget>[
Icon(Icons.delete),
Text(
'Delete Album',
),
],
),
value: 'Delete',
),
],
onSelected: (value) {
deleteAlbum(albumList.elementAt(index).id, index);
}),
],
),
),
],
),
),
);
}
}
Thank you in advance.
Welcome to the flutter. Amazing platform to start you career on building cross-platform mobile applications.
My code will look a bit different to you, but trust me, this will work out for you.
Please note: You need to change some parts, like changing the image url for NetworkImage(), onTap function, Text() content etc. But not much changes in the Whole Widget code. So please look for those, and make changes accordingly. You will get there :)
GestureDetector(
onTap: () => print('Works!'), // <-- onTap change, I have used print()
child: Container(
// height of the card which contains full item
height: MediaQuery.of(context).size.height * 0.4,
width: MediaQuery.of(context).size.width * 0.28,
// this is the background image code
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
// url is my own public image url
image: NetworkImage('https://cdn.pixabay.com/photo/2015/12/01/20/28/road-1072823__340.jpg')
),
borderRadius: BorderRadius.circular(12.0)
),
// this is the item which is at the bottom
child: Align(
// aligment is required for this
alignment: Alignment.bottomLeft,
// items height should be there, else it will take whole height
// of the parent container
child: Container(
padding: EdgeInsets.only(left: 10.0, right: 0.0),
height: MediaQuery.of(context).size.height * 0.1,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Text() using hard coded text right now
Text('Potraits', style: TextStyle(fontSize: 20.0, color: Colors.white, fontWeight: FontWeight.w500)),
SizedBox(height: 3.0),
Text('150 photos', style: TextStyle(fontSize: 17.0, color: Colors.white, fontWeight: FontWeight.w500))
]
)
),
// pop-up item
PopupMenuButton(
icon: Icon(Icons.more_vert, color: Colors.white),
itemBuilder: (_) => <PopupMenuItem<String>>[
new PopupMenuItem<String>(
child: Row(
children: <Widget>[
Icon(Icons.delete),
Text(
'Delete Album',
),
],
),
value: 'Delete',
),
],
onSelected: (value) {
//your function
}
)
]
)
)
)
)
)
Result
EDITS FOR WHOLE UI
So, the change required is in your buildRow Widget. You just need to give some paddings on your sides, and you are pretty much solid. Let me know
Widget buildRow(int index) {
return Padding(
padding: EdgeInsets.all(10.0),
child: AlbumTile(
index: index,
albumList: albumList,
deleteAlbum: _deleteAlbum,
)
);
}
And if you are unsatisfied with the spacings, just keep playing with the EdgeInsets painting class. I hope that helps. Please let me know. :)