Related
I have a somewhat complicated widget tree and can't figure this out. I've tried wrapping the Scaffold body in a SingleChildScrollView but for some reason it just makes the Container shrink and does not scroll. Here is the build function code:
return Stack(
children: [
Scaffold(
resizeToAvoidBottomInset: false,
body: Stack(
children: [
background(),
Padding(
padding: const EdgeInsets.only(top: 55),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('assets/logo.png', height: 100),
const SizedBox(width: 5),
const Text('GLOBE',
style: TextStyle(
fontSize: 40,
fontWeight: FontWeight.bold,
color: Color(0xFF7FCCDC)))
],
),
),
Padding(
padding: const EdgeInsets.only(
top: 175, left: 35, right: 35, bottom: 50),
child: Container(
decoration: BoxDecoration(
color: const Color(0xFFFCFBF4).withOpacity(0.5),
borderRadius: BorderRadius.circular(20),
),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text('Welcome!',
style: TextStyle(
fontSize: 30, color: Color(0xFF6B6FAB))),
],
),
loginForm()
],
),
),
),
],
),
),
if (_isLoading)
const Opacity(
opacity: 0.8,
child: ModalBarrier(dismissible: false, color: Colors.black),
),
if (_isLoading)
const Center(
child: CircularProgressIndicator(color: Color(0xFFb1bbd8)),
),
],
);
Return a scaffold and add a sized box of height and width same as device. As a body use stack. Then in children add the next stack.
return Scaffold(
resizeToAvoidBottomInset: false,
body: SizedBox(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Stack(
children: [
background(),
SizedBox(
height:MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: SingleChildScrollView(
child: Column(
mainAxisSize : MainAxisSize.min,
children:[
Padding(
padding: const EdgeInsets.only(top: 55),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('assets/logo.png', height: 100),
const SizedBox(width: 5),
const Text('GLOBE',
style: TextStyle(
fontSize: 40,
fontWeight: FontWeight.bold,
color: Color(0xFF7FCCDC)))
],
),
),
Padding(
padding: const EdgeInsets.only(
top: 100, left: 35, right: 35, bottom: 50),
child: Container(
decoration: BoxDecoration(
color: const Color(0xFFFCFBF4).withOpacity(0.5),
borderRadius: BorderRadius.circular(20),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text('Welcome!',
style: TextStyle(
fontSize: 30, color: Color(0xFF6B6FAB))),
],
),
loginForm()
],
),
),
),
]
)
)
if (_isLoading)
const Opacity(
opacity: 0.8,
child: ModalBarrier(dismissible: false, color: Colors.black),
),
if (_isLoading)
const Center(
child: CircularProgressIndicator(color: Color(0xFFb1bbd8)),
),
]
)
)
)
TextField has a property called scrollPadding.
scrollPadding: EdgeInsets.only(bottom: 40)
By default it is set to EdgeInsets.all(20.0)
What I want to achieve
I tried to create a sticky TabBar using StickyHeader plugin inside DraggableScrollSheet.
I try to achieve scrolling like this.
The Problem
But everytime I scrolled, there is "Incorrect use of ParentDataWidget." error.
I have tried to move the TabBar (wrapped by Container) and TabBarView (wrapped by Expanded) outside the StickyHeader and wrapped it with Column, but it doesn't works.
This is my minimal code
return Scaffold(
body: Stack(
children: [
Container(
height: double.infinity,
width: width,
decoration: BoxDecoration(
color: redMain,
),
),
DraggableScrollableSheet(
initialChildSize: 0.82,
minChildSize: 0.82,
builder: (BuildContext context, myScrollController) {
return Column(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20)),
color: Colors.white,
),
child: ListView(
controller:
myScrollController, // assign controller here
children: [
Column(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Padding(padding: EdgeInsets.only(top: 34)),
Container(
margin: EdgeInsets.only(
left: 16, right: 16, bottom: 8),
child: Text('Featured', style: Title1),
),
Container(
color: Colors.yellow,
height: 100,
),
],
),
StickyHeader(
header: Container(
margin:
EdgeInsets.only(top: 22, bottom: 22),
child: TabBar(
controller: _tabController,
labelColor: Colors.red,
unselectedLabelColor: Colors.grey,
tabs: [
Tab(
child: Text(
"All",
),
),
Tab(
child: Text(
"News",
),
),
],
),
),
content: Container(
height: 2000,
child: Expanded(
child: TabBarView(
controller: _tabController,
children: <Widget>[
Container(
width: double.infinity,
color: Colors.green,
height: height,
),
Container(
width: double.infinity,
color: Colors.blue,
height: height,
),
],
),
),
),
),
],
),
],
)),
),
],
);
},
),
],
),
);
I wrap Expanded with Container and give it "dummy" fixed height because if I only use Expanded, error occured. But actually what I want is infinite scrolling without fixed height.
content: Container(
height: 2000,
child: Expanded(
child: TabBarView(
controller: _tabController,
children: <Widget>[
Column(
The question
How to get rid of "Incorrect use of ParentDataWidget."? And how to make TabBarView infinity scroll without fixed height?
use Positioned widget inside Stack widget
I'm new to Flutter and have been trying to clone a screen that requires a scroll architecture.Here is the link of the screen that i'm working on. https://i.stack.imgur.com/1OW2b.pngThe code is as below.PS: Ignore the fields that are incomplete. As soon as I get the scroll view, i'll add the functionalities afterwards.
Scaffold(
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(15.0, 40.0, 15.0, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: const <Widget>[
Icon(Icons.menu),
Image(), //Asset image
],
),
Row(
children: <Widget>[
const Icon(Icons.notifications_none),
const SizedBox(width: 27.0),
CircleAvatar(
child: Text("JM"),
),
],
)
],
),
Row(
children: const <Widget>[
Icon(Icons.arrow_back),
SizedBox(width: 20.0),
Text("Public page"),
],
),
const SizedBox(
height: 20.0,
),
const Text(" Please fill the form following the proper order."),
const SizedBox(height: 10.0),
OverviewWidget(), //Widget with lots of TextFormField
SizedBox(height: 10.0),
DescriptionWidget(), //Widget with lots of TextFormField
],
),
),
Align([enter image description here][1]
alignment: Alignment.bottomCenter,
child: GestureDetector(
onTap: () {
print("Hello");
},
child: Container(
height: 130.0,
width: double.infinity,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey.shade400,
blurRadius: 10,
spreadRadius: 1,
)
],
),
child: Container(
margin: const EdgeInsets.only(top: 10.0, bottom: 30.0),
decoration: BoxDecoration(
color: Colors.green.shade400,
borderRadius: BorderRadius.circular(35),
),
child: const Padding(
padding: EdgeInsets.symmetric(
vertical: 15.0,
horizontal: 150.0,
),
child: Text(
"Save",
style: TextStyle(
color: Colors.white,
fontSize: 17,
fontWeight: FontWeight.w600),
),
),
),
),
),
),
],
),
);
So far, i've only created the Overview and Description widgets and i'm already getting RenderFlex overflow error. I tried wrapping the very first Column widget with a SingleChildScrollView widget but it can be noticed that the Save button at the bottom should stay fixed. I tried wrapping the whole widget with Stack widget instead of a Column widget but it didn't help either. I also used ListView widget but got the same results.What shall I do to achieve this view?
I guess you had all the elements but did not quite manage to use them as needed.
Your screen is composed of two important layout:
A stack (containing a button on top of a scrollable widget)
The scrollable widget
The stack has to be a stack, the scrollable can be anything but in your case SingleChildScrollView+Column seems the best choice.
Here is the concrete implementation:
Scaffold(
body: Stack(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(15.0, 40.0, 15.0, 0),
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(bottom: 100.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Icon(Icons.menu),
Container(
height: 100,
width: 100,
color: Colors.redAccent,
),
//Asset image
],
),
Row(
children: <Widget>[
const Icon(Icons.notifications_none),
const SizedBox(width: 27.0),
CircleAvatar(
child: Text("JM"),
),
],
)
],
),
Row(
children: const <Widget>[
Icon(Icons.arrow_back),
SizedBox(width: 20.0),
Text("Public page"),
],
),
const SizedBox(
height: 20.0,
),
const Text(" Please fill the form following the proper order."),
const SizedBox(height: 10.0),
Container(
height: 400,
color: Colors.blueAccent,
), //Widget with lots of TextFormField
SizedBox(height: 10.0),
Container(
height: 400,
color: Colors.greenAccent,
), //Widget with lots of TextFormField
],
),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: GestureDetector(
onTap: () {
print("Hello");
},
child: Container(
height: 130.0,
width: double.infinity,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey.shade400,
blurRadius: 10,
spreadRadius: 1,
)
],
),
child: Container(
margin: const EdgeInsets.only(top: 10.0, bottom: 30.0),
decoration: BoxDecoration(
color: Colors.green.shade400,
borderRadius: BorderRadius.circular(35),
),
child: const Padding(
padding: EdgeInsets.symmetric(
vertical: 15.0,
horizontal: 150.0,
),
child: Text(
"Save",
style: TextStyle(
color: Colors.white, fontSize: 17, fontWeight: FontWeight.w600),
),
),
),
),
),
),
],
),
)
Note that you have to put a bottom padding inside the SingleChildScrollView to take into account the button. This is fine in your case since the button has a static height. However if you need something more fancy you might want to check How to create a scroll view with fixed footer with Flutter? as #Csisanyi suggested
Consider wrapping the Column with an Expanded widget
You can also check out this flutter doc. It addresses this common issue in detail.
https://flutter.dev/docs/testing/common-errors
Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(15.0, 40.0, 15.0, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Icon(Icons.menu),
// Image(), //Asset image
Image.asset(
'assets/ocean.jpg',
height: kToolbarHeight,
/// make sure of image height
fit: BoxFit.fitHeight,
),
],
),
Row(
children: <Widget>[
const Icon(Icons.notifications_none),
const SizedBox(width: 27.0),
CircleAvatar(
child: Text("JM"),
),
],
)
],
),
Row(
children: const <Widget>[
Icon(Icons.arrow_back),
SizedBox(width: 20.0),
Text("Public page"),
],
),
const SizedBox(
height: 20.0,
),
const Text(
" Please fill the form following the proper order."),
const SizedBox(height: 10.0),
//todo: OverviewWidget(), //Widget with lots of TextFormField
...List.generate(22, (index) {
return TextField(
decoration: InputDecoration(hintText: "1st $index"),
);
}),
SizedBox(height: 10.0),
//todo: DescriptionWidget(), //Widget with lots of TextFormField
...List.generate(22, (index) {
return TextField(
decoration: InputDecoration(hintText: "2st $index"),
);
}),
],
),
),
Align(
alignment: Alignment.bottomCenter,
child: GestureDetector(
onTap: () {
print("Hello");
},
child: Container(
height: 130.0,
width: double.infinity,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey.shade400,
blurRadius: 10,
spreadRadius: 1,
)
],
),
child: Container(
margin: const EdgeInsets.only(top: 10.0, bottom: 30.0),
decoration: BoxDecoration(
color: Colors.green.shade400,
borderRadius: BorderRadius.circular(35),
),
child: const Padding(
padding: EdgeInsets.symmetric(
vertical: 15.0,
horizontal: 150.0,
),
child: Text(
"Save",
style: TextStyle(
color: Colors.white,
fontSize: 17,
fontWeight: FontWeight.w600),
),
),
),
),
),
),
],
),
),
),
I use NestedScrollView, I have TabView in the body. If the widget in the tabview is half-empty, then an empty space remains.
As far as I understand, this is due to the fact that my headerSliverBuilder is too long, and the tabview is created at the full height of the screen.
I want to make the same functionality as in the Instagram profile. TabView with posts, saves.
SafeArea(
bottom: IphoneHasNotch.hasNotch ? false : true,
child: NestedScrollView(
headerSliverBuilder: (context, value) {
return [
SliverToBoxAdapter(
child: Container(
decoration: BoxDecoration(
color: Colors.white
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 15),
child: CreateHeaderWithOutBack(
title: 'Profile',
action: GestureDetector(
onTap: () => showModal(),
child: Icon(FeatherIcons.moreVertical, size: getWidth(25)),
)
),
)
]
)
)
),
SliverToBoxAdapter(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: getHeight(180),
width: MediaQuery.of(context).size.width,
child: Stack(
children: [
ClipRect(
child: ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Image.network(userData.avatar != null && userData.avatar.length > 0 ? userData.avatar : 'https://klike.net/uploads/posts/2019-05/1556708032_1.jpg',fit: BoxFit.cover, width: MediaQuery.of(context).size.width,height: getHeight(140),)
),
),
Positioned.fill(
child: Align(
alignment: Alignment.bottomLeft,
child: InkWell(
child: Hero(
tag: 'imageHero',
child: Container(
margin: EdgeInsets.symmetric(horizontal: 20),
height: getWidth(102),
width: getWidth(102),
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle
),
padding: EdgeInsets.all(2),
child: Center(
child: CreateImageAvatar(name: userData.name, url: userData.avatar, size: getWidth(100),)
),
)
),
onTap: () {
Navigator.push(
context,PageRouteBuilder(pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) => DetailScreen(userData.avatars),
opaque: false
));
},
)
),
)
],
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 10),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(userData.name + ', ' + userData.age.toString(),
style: TextStyle(fontSize: 21, fontWeight: FontWeight.w600),
),
userData.city != null && userData.city.length > 0 ? Padding(
padding: const EdgeInsets.only(top: 2,bottom: 10),
child: Text(userData.city,
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: kTextColor.withOpacity(0.6)),
),
) : Container(),
userData.company != null && userData.company.length > 0 ? Padding(
padding: const EdgeInsets.only(bottom: 5),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SvgPicture.asset('assets/images/work.svg', color: Color(0xFFBEBEBE), width: getWidth(15),),
SizedBox(width: 5),
Text(userData.company,
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xFFBEBEBE)),
),
],
),
) : Container(),
],
),
),
widget.id == -1 ?GestureDetector(
onTap: (){
Provider.of<AppData>(context,listen: false).updateUserDataProfile(userData);
Navigator.pushNamed(context, EditProfile.routeName);
},
child: Container(
decoration: BoxDecoration(
color: Color(0xFFF6F6F6),
borderRadius: BorderRadius.circular(5),
border: Border.all(color: Color(0xFFEFEFEF), width: 1)
),
padding: EdgeInsets.symmetric(vertical: 8,horizontal: 15),
child: Text('Edit',
style: TextStyle(color: Color(0xFF9E9E9E), fontWeight: FontWeight.w500, fontSize: getHeight(12)),
),
),
) : Container()
],
),
userData.bio != null && userData.bio.length > 0 ? Padding(
padding: const EdgeInsets.only(top: 5),
child: Text(userData.bio,
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xFF6D6D6D)),
),
) : Container(),
SizedBox(height: 20),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CreateBadgeProfile(title: userData.friends.toString(), disc: 'friends',),
SizedBox(width: 10),
CreateBadgeProfile(title: userData.news.toString(), disc: 'posts',),
],
),
],
),
)
],
),
),
SliverPadding(
padding: EdgeInsets.only(top: 25),
sliver: SliverToBoxAdapter(
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Color(0xFFEAEAEA),
width: 1.0,
),
),
),
child: TabBar(
indicatorWeight: 2.0,
unselectedLabelColor: kTextColor.withOpacity(0.4),
labelStyle: TextStyle(fontFamily: 'Gilroy',fontWeight: FontWeight.w500, fontSize: 14, color: kTextColor.withOpacity(0.4)),
controller: _tabController,
tabs: <Widget> [
Tab(text: 'About'),
Tab(text: 'Posts'),
]
labelColor: kTextColor,
indicatorColor: kPrimaryColor,
),
),
),
)
];
},
body: Container(
child: TabBarView(
controller: _tabController,
children: [
Bio(userData: userData,),
NewsFeed(id:widget.id,post: 'post'),
],
),
)
),
);
I am new in a flutter, now when I am trying to display multiple cards inside a ListView on each tab in TabBarView I get this error
" Controller's length property (4) does not match the number of tabs (5) present in TabBar's tabs property. "
and here is my code, is there a way to do this?
this is the TabBar
child: TabBar(
tabs: [
Tab(child: Text("New", style: TextStyle(color: Colors.black),),),
Tab(child: Text("Indoor", style: TextStyle(color: Colors.black),),),
Tab(child: Text("Outdoor", style: TextStyle(color: Colors.black),),),
Tab(child: Text("Furniture", style: TextStyle(color: Colors.black),),),
],
indicatorColor: Color.fromRGBO(230, 0, 49, 0.86),
),
and this is the TabBarView
child: TabBarView(
children:
[
//First tab
ListView(
children: [
Card(
clipBehavior: Clip.antiAlias,
elevation: 0.5,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.only(
left: 0.0, top: 10.0, bottom: 0.0, right: 0.0),
child: InkWell(
onTap: () {
},
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(alignment: Alignment.bottomCenter, children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0),
child: Ink.image(
height: 200,
image: AssetImage('assets/images/kitchen.jpg'),
fit: BoxFit.fitWidth),
),
]),
ListTile(
title: Text(
"Project Name",
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontWeight: FontWeight.bold),
),
subtitle: Text(
"Project details descriptions",
style: TextStyle(
fontWeight: FontWeight.w500,
color: Colors.grey),
),
),
],
),
),
),
),
],
),
ListView(
children: [
Card(
clipBehavior: Clip.antiAlias,
elevation: 0.5,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.only(
left: 0.0, top: 10.0, bottom: 0.0, right: 0.0),
child: InkWell(
onTap: () {
},
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(alignment: Alignment.bottomCenter, children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0),
child: Ink.image(
height: 200,
image: AssetImage('assets/images/food_tables.jpg'),
fit: BoxFit.fitWidth),
),
]),
ListTile(
title: Text(
"Project Name",
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontWeight: FontWeight.bold),
),
subtitle: Text(
"Project details descriptions",
style: TextStyle(
fontWeight: FontWeight.w500,
color: Colors.grey),
),
),
],
),
),
),
),
],
),
],
),
You have 4 tabs but you have 2 listview in tabbarViews.
For solution you have to remove 2 tabs or you can add 2 more widget in tabbarview
TabBarView(children: [
// 1.tab
Container(),
// 2.tab
Container(),
// 3.tab
Container(),
// 4.tab
Container(),
]);