ListView with Expanded when not scrollable - flutter

I want to make a menu with list of ExpansionTile and a footer, when the ExpansionTile is not expanded (not scrollable) the footer stay on the bottom of screen, but when the ExpansionTile is expanded (scrollable) the children of ExpansionTile push the footer out of screen.
For a clearer illustration, please see the following image
this my current code, but it's not a best way I think
checking is scrollable or not
checkIsScrollable(BuildContext context) {
if (Scrollable.of(context).position.minScrollExtent >=
Scrollable.of(context).position.maxScrollExtent) {
setState(() {
scrollable = false;
});
} else {
setState(() {
scrollable = true;
});
}
this the listview
ListView(
padding: EdgeInsets.symmetric(horizontal: 16),
children: [
LimitedBox(
maxHeight: !scrollable
? MediaQuery.of(context).size.height - 84
: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 24,
),
Text(
"Account".toUpperCase(),
style: ThemeApp.appBarTextStyle(),
),
SizedBox(
height: 24,
),
photoProfile(),
SizedBox(
height: 40,
),
menuProfile(),
SizedBox(
height: 40,
),
AccountContent.customerCare1(
context: context,
onExpansionChanged: (_) {
Future.delayed(
Duration(milliseconds: 101),
() {
checkIsScrollable(context);
},
);
},
),
AccountContent.sellWithUs(
context: context,
onExpansionChanged: (_) {
Future.delayed(
Duration(milliseconds: 101),
() {
checkIsScrollable(context);
},
);
},
),
AccountContent.customerCare2(
context: context,
onExpansionChanged: (_) {
Future.delayed(
Duration(milliseconds: 101),
() {
checkIsScrollable(context);
},
);
},
),
!scrollable
? Expanded(
child: Container(),
)
: Container(),
Container(
padding: EdgeInsets.symmetric(vertical: 16),
child: Column(
children: [
InkWell(
child: Row(
children: [
Container(
height: 18,
width: 18,
color: ThemeApp.borderColor,
),
SizedBox(width: 10),
Text(
"LOGOUT",
style: ThemeApp.subTitleTextStyle(),
),
],
),
onTap: () {},
),
SizedBox(
height: 20,
),
Row(
children: [
Expanded(
child: BorderedButton(
child: SvgPicture.asset(
"assets/icons/icon_whatsapp.svg",
color: ThemeApp.primaryColor,
),
onTap: () {
openWhatsapp();
},
),
),
SizedBox(
width: 17,
),
Expanded(
child: BorderedButton(
child: SvgPicture.asset(
"assets/icons/icon_instagram.svg",
color: ThemeApp.primaryColor,
),
onTap: () {
openInstagram();
},
),
),
SizedBox(
width: 17,
),
Expanded(
child: BorderedButton(
child: SvgPicture.asset(
"assets/icons/icon_mail.svg",
color: ThemeApp.primaryColor,
),
onTap: () {
openMail();
},
),
),
],
),
],
),
)
],
),
),
],
);

You should use a Column inside of LayoutBuilder and with a ConstrainedBox, set The minHeight of the Column to maxHeight of LayoutBuilder:
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
YourListView(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
),
YourFooter(),
],
),
),
);
},
);
}

I didn't get your question correctly but i guess you need too use Stack widget which always stays your footer on it's place even your page is Scrollable.

The footer you don't want to scroll, make the Column having two children and inside the column add another column and put the scrollable data inside that and wrap that with the SingleChildScrolView.
Column(
children: [
SingleChildScrollView(
child: Column(
children: [
// your scrollable data here
],
Column(
children: [
// Footer not scrollable
],),
],

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.

Flutter Infinite Grid View Pagination

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 :)

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: Expanded IconButton still shows under the Container

AnimatedContainer(
...
child: Container(
child: Column(
children: [
Row(
children: [
Container(
width:60,
height:50,
color: Colors.black,
),
],
),
Expanded(
child: GestureDetector(
onTap: () {
print('what');
},
child: Container(
height: 50,
child: Column(
children: [
Expanded(
child: Text('asdf'),
),
Expanded(
child: IconButton(
icon: Icon(Icons.ac_unit),
onPressed: () {},
),
I have this AnimatedContainer widget here. My Text() widget and RaisedButton widget are hidden inside the box. They appear as the AnimatedContainer expand. However, my IconButton doesn't.
Also, Icon widget also stays like IconButton widget. How can I make it appear when the AnimatedContainer is stretched like the Text widget?
I've modified your code, hope this is what u want.
class _RowStructureState extends State<RowStructure> {
bool pressed = false;
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
pressed = !pressed;
});
print(pressed);
},
child: Column(children: <Widget>[
AnimatedContainer(
height: pressed ? 120 : 50,
color: Colors.green,
duration: Duration(seconds: 1),
child: Stack(
children: [
Container(
height: 50,
width: 50,
color: Colors.black,
),
Positioned(
bottom:0,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text('asd'),
IconButton(
padding: EdgeInsets.all(0),
icon: Icon(Icons.ac_unit,),
onPressed: pressed?(){}:null,
),
],
),
)
],
),
)
]),
);
}
}

How to make each item in listview as column flutter

I work in listview in flutter and because I am a new developer in flutter, I face some difficulties in designing the listview.Now the list works with me, As follows:
Now it's as Row all data see as Row or in one line. I try to make it like column each item in it as Separate column.So will be come first Column image as center , second Column text , third Column text like that.From top to bottom.
This is my code:
#override
Widget build(BuildContext context) {
String getID;
getID= widget.itemHolder.toString();
return FutureBuilder<List<Flowerdata>>(
future: fetchFlowers(),
builder: (context, snapshot) {
if (!snapshot.hasData) return Center(
child: CircularProgressIndicator()
);
return ListView(
children: snapshot.data.map((data) => Column(children: <Widget>[
GestureDetector(
onTap: ()=>{getItemAndNavigate(data.id ,context)
},
child: Row(
children: [
Container(
width: 100,
height: 100,
margin: EdgeInsets.fromLTRB(10, 0, 10, 0),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child:
Image.network(data.ImageURL,
width: 200, height: 100, fit: BoxFit.cover,))),
Flexible(child:
Text(data.Name,
style: TextStyle(fontSize: 18)
)
),
Flexible(child:
Text(data.SendFrom,
style: TextStyle(fontSize: 18)
)
),
FloatingActionButton(
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => AddComments( UserPostID: UserPostID,psoid:psoid.toString()),));
},
child: Icon(Icons.add),
),
]
),
),
Divider(color: Colors.black),
],))
.toList(),
);
},
);
}
}
If anyone knows the solution to a problem please help me.
Try this
return ListView(
children: [
for (var data in snapshot.data)
GestureDetector(
child: Column(
children: [
Row(),
Text(data),
Text(data),
Text(data),
],
),
)
],
);
OR
return ListView(
children: [
snapshot.data.map((data) => GestureDetector(
child: Column(
children: [
Row(),
Text(data),
Text(data),
Text(data),
],
),
))
],
);
just add Scaffold on page top level
Scaffold(
body: ListView
)