i want a slidingUpPanel similar to YouTube comments section. that not open with the scrolling of the content but open and close only from the drag handle by tap or by swipe up/down).
the drag handle is the top section of the slidingUpPanel :
so the draggable should happen only from this drag handle not from the whole body. (you can try the code below to know what i mean)
it means if i scroll the body(the listView builder) the slidingUpPanel keep its place (either open or closed) until i swip it from the drag handle.
my current code:
PanelController panelController = PanelController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Stack(
children: [
SlidingUpPanel(
controller: panelController,
maxHeight: MediaQuery.of(context).size.height * .9,
minHeight: MediaQuery.of(context).size.height * .7,
borderRadius: BorderRadius.vertical(top: Radius.circular(40.0)),
panelBuilder: (controller) => Stack(
alignment: Alignment.topCenter,
children: [
GestureDetector(
onTap: () {
panelController.isPanelOpen
? panelController.close()
: panelController.open();
},
child: Container(
color: Colors.transparent,
height: 36.0,
width: MediaQuery.of(context).size.width,
child: Center(
child: Container(
width: 46.77,
height: 4.0,
decoration: BoxDecoration(color: Colors.grey),
),
),
),
),
Padding(
padding: const EdgeInsets.only(top: 40.0),
child: ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
controller: controller,
padding: EdgeInsets.zero,
itemCount: 20,
itemBuilder: (context, index) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 70.0,
color: Colors.red,
),
),
],
);
}),
),
],
)),
],
),
));
}
Related
I am trying to extend Stack a little bit down to create room for "All caught up" text.
Here is my code;
return Stack(
children: [
buildListView(scrollController),
buildBackToTop(scrollController, backtoTop),
buildBottomReached(isLastIndex),
],
);
Widget buildBackToTop(ScrollController scrollController, bool backtoTop) {
if (backtoTop) {
return Align(
alignment: Alignment.topCenter,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: FloatingActionButton.extended(
onPressed: () {
scrollController.animateTo(0,
duration: const Duration(milliseconds: 200),
curve: Curves.linear);
},
label: const Text("Back to Top"),
),
),
);
} else {
return const SizedBox();
}
}
Widget buildBottomReached(bool isLastIndex) {
if (isLastIndex) {
return const Align(
alignment: Alignment.bottomCenter,
child: Text(
"All caught up 🎉",
style: TextStyle(
color: Colors.blue, fontWeight: FontWeight.bold, fontSize: 20.00),
),
);
} else {
return const SizedBox();
}
}
Widget buildListView(ScrollController scrollController) {
return ListView.builder(
controller: scrollController,
itemCount: 50,
itemBuilder: (context, index) {
return SizedBox(
width: 20,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: 20,
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: Colors.grey[100],
),
child: Center(
child: Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("Index $index"),
),
),
),
),
),
);
},
physics: const BouncingScrollPhysics());
}
When I do following,
return Stack(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 30),
child: buildListView(scrollController),
),
buildBackToTop(scrollController, backtoTop),
buildBottomReached(isLastIndex),
],
);
This is desired but it adds whitespace to bottom all the time,
Is there a way to do this?
another valid solution for your case is to expand the Stack to the screen size, then align your widgets with the Align widget like this:
final mq = MediaQuery.of(context);
return ConstrainedBox(
constraints: BoxConstraints(
maxHeight: mq.size.height,
minHeight: mq.size.height,
maxWidth: mq.size.width,
minWidth: mq.size.width,
),
child: Stack(
fit: StackFit.expand,
children: [
buildListView(scrollController),
Align(
alignment: Alignment.bottomCenter,
child: buildBackToTop(scrollController, backtoTop),
),
buildBottomReached(isLastIndex),
],
),
);
Based on your UI. try Column widget instead of Stack.
return Column(
children: [
Expaneded(child: buildListView(scrollController)),
buildBackToTop(scrollController, backtoTop),
buildBottomReached(isLastIndex),
],
);
If it needs overlay, use Stack by combining listView and Text with Column.
Wrap the widget with Positioned and add bottom.
return Stack(
children: [
.....
Positioned(
bottom: yourValue,
child:
First, wrap Stack with ListView widget
Then, move buildBottomReached helper as a sibling of the Stack widget
return ListView(//this
children: [
Stack(
children: [
buildListView(),
buildBackToTop(backtoTop),
//buildBottomReached(isLastIndex),//move this a sibling of the Stack
],
),
buildBottomReached(isLastIndex),//moved here
],
);
Then, on buildListView helper, set ListView.builder property shrinkWrap to true.
Widget buildListView(ScrollController scrollController) {
return ListView.builder(
shrinkWrap: true,//this
controller: scrollController,
itemCount: 50,
itemBuilder: (context, index) {
return SizedBox(
width: 20,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: 20,
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: Colors.grey[100],
),
child: Center(
child: Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("Index $index"),
),
),
),
),
),
);
},
physics: const BouncingScrollPhysics());
}
it's possible to make listview in expand and fill the container without overflow the container, and shrink when listview is short of content without declare manual min or max height?
Code
Flexible(
fit: FlexFit.loose,
child: Container(
padding: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(kBoxRadius),
boxShadow: [kBoxShadow]),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
DText(
"Aug 2021",
size: 12,
weight: FontWeight.bold,
),
SizedBox(height: 10),
Row(
children: dates,
),
SizedBox(height: 15),
Container(
height: 1,
color: Color(0xFFE7E7E7),
),
Container(
constraints:
BoxConstraints(minHeight: 0, maxHeight: 600),
child: ListView.separated(
shrinkWrap: true,
itemBuilder: (context, index) {
return DText("Asd");
},
separatorBuilder: (_, __) {
return SizedBox(height: 10);
},
itemCount: 20,
),
)
],
),
),
)
Expectation
when the content is big
because i set manually max height.
Yes, it is possible using shrinkWrap: true. The code will be something like below,
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(),
body: Center(
child: Container(
color: Colors.red,
child: ListView(
shrinkWrap: true,
children: <Widget>[
ListTile(title: Text('Apple')),
ListTile(title: Text('Orange')),
ListTile(title: Text('Banana')),
],
),
),
),
),
);
}
}
I am trying to create a chat screen with text field at the bottom and whenever it is clicked the keyboard comes up and the textfield should be on top of the keyboard but my own isn't doing what it's supposed to do. I have checked other similar questions and answers on google, stackoverflow and github but it still stays the maybe because in mine am using constraints: BoxConstraints(maxHeight: 160), which might be what is causing it but i don't really know
This my code
class _ChatSectionState
extends State<ChatSectionState> {
List<File> images = List<File>();
getMultiFile() async {
await pickFileFromMobile("multi").then((value) {
if (value != null) {
value.paths.forEach((element) {
images.add(File(element));
});
// images = value.paths.map((path) => File(path)).toList();
}
});
setState(() {});
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(...,
body: Container(
height: double.infinity,
child: Stack(
children: [
Padding(
padding: EdgeInsets.only(bottom: 50),
child: Container(
child: ListView.builder(
itemCount: 10,
reverse: true,
shrinkWrap: true,
physics: ScrollPhysics(),
itemBuilder: (ctx, idx) {...
///...
),
),
),
///This is the part that hides behide the keyboard when it appears which i want to show on top
Align(
alignment: Alignment.bottomCenter,
child: Container(
decoration: BoxDecoration(
color: white,
border: Border(
top: BorderSide(
color: grey[400],
width: .7,
),
bottom: BorderSide(
color: grey[400],
width: .7,
)),
),
constraints: BoxConstraints(maxHeight: 160), //This is the box contraints
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
(images.length < 1)
? SizedBox(height: 0)
: Expanded(
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
physics: ScrollPhysics(),
padding: EdgeInsets.symmetric(
vertical: 7, horizontal: 10),
itemCount: images.length,
itemBuilder: (ctx, idx) {
return Container(
///... This is to show a image from file width a height of 100
);
},
),
),
Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: grey[300],
width: .5,
)),
),
child: Row(children: [
Container(
margin: new EdgeInsets.symmetric(horizontal: 8.0),
child: new IconButton(
icon: new Icon(Icons.image),
onPressed: () => getMultiFile(),
color: primary,
),
),
Flexible(
child: Container(
child: TextFormField(
//controller: messageEditingController,
style: TextStyle(color: nBlack, fontSize: 15.0),
decoration: InputDecoration.collapsed(
hintText: 'Type a message',
hintStyle: TextStyle(color: grey),
),
onFieldSubmitted: (value) {},
),
),
),
Container(
margin: new EdgeInsets.symmetric(horizontal: 8.0),
child: new IconButton(
icon: new Icon(Icons.send),
onPressed: () {},
color: primary,
),
),
]),
),
],
),
),
)
],
),
),
resizeToAvoidBottomInset: false,
));
}
}
Please how can I make it work cause i have checked different pages still does give me what i want. I also tried this [answer][1]
[1]: https://stackoverflow.com/a/49715952/14650702 which worked but the animation was terrible
You can change resizeToAvoidBottomInset: ture, for scaffold.
it will shrink the whole scaffold when keyboard is opened.
I use another way. resizeToAvoidBottomInset: true and do some changes for lifting widgets top of keyboard.
bottom = MediaQuery.of(context).viewInsets.bottom;
Scaffold(
resizeToAvoidBottomInset: ture,
body: SingleChildScrollView(
reverse:true,
child: Padding(
padding: EdgeInsets.only(bottom: bottom),
child: ...
)));
if you decrease space from keyboard to last widget use MediaQuery.of(context).viewInsets.bottom/2 e.x.
My UI use case is similar to an Instagram user profile.
I have multiple widget on top and an infinite ListView.builder bellow with item.
I would like to scroll everything like within a ScrollView but the scrollview can not contain an infinite list.
How can I handle this situation in Flutter ?
Using CustomScrollView is the Solution
Here i have done Basic Implementation for you :
class MainBody extends StatefulWidget {
MainBody({Key key}) : super(key: key);
#override
_MainBodyState createState() => _MainBodyState();
}
class _MainBodyState extends State<MainBody> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
child: Container(
padding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Container(
height: 100,
width: 100,
decoration: BoxDecoration(
shape: BoxShape.circle, color: Colors.green),
child: Center(child: Text("ProfileImage")),
),
flex: 1,
),
Expanded(flex: 2, child: Text("Profile Statistics"))
],
),
Text("Bio"),
Text("Some Buttons"),
Text("Story Highlights"),
])),
),
SliverToBoxAdapter(
child: Container(
height: 150,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 10,
itemBuilder: (context, index) {
return Container(
margin: const EdgeInsets.all(10),
height: 100,
width: 100,
decoration: BoxDecoration(
shape: BoxShape.circle, color: Colors.red),
child: Center(child: Text((index + 1).toString())),
);
})),
),
SliverAppBar(
centerTitle: false,
pinned: true,
flexibleSpace: DefaultTabController(
initialIndex: 0,
length: 2,
child: TabBar(tabs: [
Center(child: Text("Icon1")),
Center(child: Text("Icon2")),
]),
),
),
SliverPadding(
padding: const EdgeInsets.all(8),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => Container(
height: 30,
child: Center(child: Text("Hey" + index.toString()))),
childCount: 20)),
)
],
),
);
}
}
Futher Enhancements can be done
If I drag and hold my finger down I can see a few items that are below the cutoff of the screen but as soon as I let go, it just bounces back to the top. I tried using SingleChildScrollView places, tried setting primary = true, and a bunch of other stuff that didn't help. I'm fairly new to flutter so any help would be appreciated!! Let me know if any more info is needed.
Here is my code:
import 'package:flutter/material.dart';
import 'package:drink_specials/models/restaurant.dart';
import 'package:drink_specials/screens/home/restaurant_list.dart';
class RestaurantNameTextStyle {
static TextStyle display5(BuildContext context) {
return Theme.of(context).textTheme.headline2.copyWith(color: Colors.white);
}
}
class RestaurantTypeTextStyle {
static TextStyle display5(BuildContext context) {
return Theme.of(context).textTheme.headline6.copyWith(color: Colors.white);
}
}
class RestaurantDetail extends StatelessWidget {
final Restaurant restaurant;
RestaurantDetail({Key key, #required this.restaurant}) : super(key: key);
#override
Widget build(BuildContext context) {
final topContentText = Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(height: 100.0),
Text(
restaurant.name,
style: RestaurantNameTextStyle.display5(context),
),
SizedBox(height: 10.0),
Expanded(
flex: 6,
child: Padding(
padding: EdgeInsets.only(left: 10.0),
child: Text(
restaurant.restaurant_type,
style: RestaurantTypeTextStyle.display5(context),
))),
],
);
final topContent = Stack(
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 10.0),
height: MediaQuery.of(context).size.height * 0.5,
decoration: new BoxDecoration(
image: new DecorationImage(
image: NetworkImage(restaurant.photo),
fit: BoxFit.cover,
),
)),
Container(
height: MediaQuery.of(context).size.height * 0.5,
padding: EdgeInsets.all(40.0),
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(color: Color.fromRGBO(58, 66, 86, .9)),
child: Center(
child: topContentText,
),
),
Positioned(
left: 8.0,
top: 60.0,
child: InkWell(
onTap: () {
Navigator.pop(context);
},
child: Icon(Icons.arrow_back, color: Colors.white),
),
)
],
);
final bottomContent = Container(
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.all(8.0),
child: Center(
child: ListView.builder(
scrollDirection: Axis.vertical,
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
itemCount: restaurant.specials.length,
itemBuilder: (context, index) {
final item = restaurant.specials[index];
return Card(
elevation: 8.0,
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Container(
decoration: BoxDecoration(color: Color.fromRGBO(58, 66, 86, 1.0)),
child: ListTile(
contentPadding: EdgeInsets.symmetric(horizontal:20, vertical:10),
title: Text(item, style: TextStyle(color: Colors.white)),
)
),
);
}
),
),
);
return Scaffold(
body: Column(
children: <Widget>[
topContent,
Expanded(
child: bottomContent,
),
],
),
);
}
}
There is a ListView inside a SingleChildScrollView and both of them are scrollable. Scrolling on one of them should be disabled.
As they already explained. If you have a ListView.builder, you don't need SingleChildScrollView.
Try removing SingleChildScrollView. The code should look like this:
Scaffold(
body: Column(
children: <Widget>[
topContent,
Expanded(
child: bottomContent,
),
],
),
);
ListView already have scroll behavior so you won't need some SingleChildScrollView