DraggableScrollableSheet with Column won't drag to top - flutter

I'm trying to create a draggable bottom sheet that will have a set header & footer. Between those, the body/content will vary. There may be an empty state or there may be a list of data. The user should be able to expand this sheet to the top of the screen as well.
Here is a base example where I can see all the data, but I can't get it to drag up to the max size. I can only scroll within that ListView between the header & footer.
How can I update this so I can still drag the sheet to snap to the top, or back to the middle, or dismiss?
Code Example:
void _showDraggableSheet(context) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
useRootNavigator: true,
builder: (_) {
return DraggableScrollableSheet(
expand: false,
initialChildSize: 0.65,
minChildSize: 0.65,
maxChildSize: 0.9,
builder: (_, controller) {
return Container(
color: Colors.black,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
_buildHeader(),
_buildContent(),
_buildFooter(),
],
),
);
},
);
},
);
}
Widget _buildContent() {
return Expanded(
child: Container(
color: Colors.green,
child: ListView.builder(
itemCount: 100,
itemBuilder: (_, i) => ListTile(title: Text('Item $i')),
),
),
);
}

Related

Flutter Grid View Reorder & Drag Drop onto Another Item and Merge

I have been trying to add drag/drop support to my app, currently what I have come with is using this library:
reorderable_grid_view
I used this example code:
code link
The reason I used this library is that it's smooth enough of animations when dragging. But what I want to do is to drag one item to another so that I can merge the one to another object when I drop. (It's like in Android/iOS home screen where you can drag apps to folders or drag into another that it creates a folder)
I have searched all the site but couldn't come across with such thing, only drag/drop libraries are available. Can anyone help me on this?
Thanks in advance.
class MyHomePage extends StatelessWidget {
MyHomePage({super.key});
final ValueNotifier<List<ValueNotifier<List<Widget>>>> items = ValueNotifier([
ValueNotifier([Text("A")]),
ValueNotifier([Text("B")]),
ValueNotifier([Text("C")]),
ValueNotifier([Text("D")]),
]);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
child: ValueListenableBuilder(
valueListenable: items,
builder: (BuildContext context, List<ValueNotifier<List<Widget>>> folders, Widget? child) {
return GridView.builder(
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
itemCount: folders.length,
itemBuilder: (context, index) {
ValueNotifier<List<Widget>> item = folders[index];
return LongPressDraggable(
delay: const Duration(milliseconds: 500),
feedback: SizedBox(width: MediaQuery.of(context).size.width / 4, height: MediaQuery.of(context).size.width / 4, child: FittedBox(child: Icon(Icons.folder))),
data: index,
childWhenDragging: const SizedBox(),
child: DragTarget(
onAccept: (data) {
List<Widget> alreadyHaved = item.value;
alreadyHaved.addAll(folders[data as int].value);
item.value = alreadyHaved;
items.value.removeAt(data);
items.notifyListeners();
},
builder: (context, candidateData, rejectedData) {
return ValueListenableBuilder(
valueListenable: item,
builder: (BuildContext context, List<Widget> boxValues, Widget? child) {
return Stack(children: [
const Positioned.fill(
child: FittedBox(
child: Icon(
Icons.folder,
color: Colors.amber,
))),
Positioned.fill(
child: LayoutBuilder(
builder: (p0, p1) => SizedBox(
height: p1.maxHeight * .7,
width: p1.maxWidth * .7,
child: Center(
child: Wrap(
children: boxValues,
),
))),
)
]);
},
);
},
),
);
});
},
),
),
),
],
),
);
}
}

ListView.Builder can't scroll to max extent

I'm trying to create a contact book page that will load recent contact and all of my contact list, I managed to get the data and build it into the widget.
Design that I want to achieve for the screen body are:
a search widget which always stay on top even when the contact book can be scrolled.
List of the contact that can be scrolled
The problem is when there are a lot of contact (let say 20 contacts). If the screen size is small, it can only scroll until 7-8 contacts, but I know there's more to it. But I guess my ListView.Builder won't let me scroll down anymore. Like it's constrained by some properties but I don't know what is the reason
Can anyone please tell me the problem and how to fix this ?
Here's my code:
/// BODY
body: WillPopScope(
onWillPop: () {
print('hello');
},
child: ListView(
physics: BouncingScrollPhysics(),
shrinkWrap: true,
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 20),
height: MediaQuery.of(context).size.height,
child: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (context, isScrolled) {
return [
SliverPersistentHeader(
pinned: true,
floating: false,
delegate: ChooseUsersHeaderDelegate(
widgetList: Container(
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// Build Search Box Function
buildSearch(),
],
),
),
minHeight: selectedContactId.isNotEmpty ? 135 : 60,
maxHeight: selectedContactId.isNotEmpty ? 135 : 60,
parentContext: context,
),
),
];
},
body: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// All Contacts Subtitle
Text(
'allContacts',
style: GoogleFonts.montserrat(
fontSize: 14,
fontWeight: FontWeight.w500,
color: primaryColor,
fontStyle: FontStyle.normal,
),
).tr(),
/// All Contacts
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
isLoading
? ReusableLoadingContact()
: NotificationListener<
OverscrollIndicatorNotification>(
onNotification: (overScroll) {
overScroll.disallowIndicator();
return true;
},
/// Call function to return the widget for building contact list
child: buildWidget(context, 'contacts')),
],
),
],
),
),
),
)
],
),
),
Here's the code for building the contact List:
Widget build(BuildContext context) {
return contact.isEmpty
? Text('')
: ListView.builder(
/// Force to build only 2 items if recent, otherwise build all
itemCount: status == 'recent' ? contact.length > 2 ? 2 : contact.length : contact.length,
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
/// Return the widget of each contact
},
);
}
if you want to make your search bar stay on top here a very simple way
body : Column(
children:[
// this part will stay on top
Container() // your widget search bar
// listview widget will rendered after the Container
Expanded(
child: Listview.builder(
// this part will be scrollable
// return the widget contact
)
)
]
....
To attain this , use Container and Expanded as child widgets for Column
Format:
body : Column(
children:[
Container(
child: // Your search bar goes here
),
Expanded(
child: // Your list view goes here
)
)
]

conditional rendering List with flutter

I have a model of 'contracts' and one of the parameters is if the contract is active/inactive. I did this condition using a bool true/false in the contractModel file.
Now, I am rendering a ListView.Builder with all the contracts, but I have created a filter at the top of the screen to select the contracts that are active/inactive. What I want to achieve is to render the active contracts in the 'active' ListView.Builder and the inactive contracts in the 'inactive' ListView.Builder.
This is the code for the ListView.Builder:
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Categories(),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: contracts.length,
itemBuilder: (context, index) => ContractCard(
contract: contracts[index],
press: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailsScreen(
contract: contracts[index],
),
),
),
),
),
),
),
],
);
}
}
Any help is appreciated.
Reference package(flutter package), explanation from the author (medium post).This is how you would normally conditionally render widgets
return Column(
children: <Widget>[
someCondition == true ?
Text('The condition is true!'):
Text('The condition is false!'),
],
);
with the above package though you would instead do
return Column(
children: <Widget>[
Conditional.single(
context: context,
conditionBuilder: (BuildContext context) => someCondition == true,
widgetBuilder: (BuildContext context) => Text('The condition is true!'),
fallbackBuilder: (BuildContext context) => Text('The condition is false!'),
),
],
);

How to constrain height of AlertDialog

I show dialog with list inside it.
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(select_conference),
content: ListView.separated(
itemCount: selected.length,
separatorBuilder: (context, index) => CommonDivider(),
itemBuilder: (context, index) => ListTile(...),
),
);
});
But no matter how many elements is has - dialog fills all available height. Is there any solution to solve this without calculating height of list elements?
Wrap your content with a Column and set mainAxisSize to MainAxisSize.min:
AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
//your content
],
),
You can wrap it inside a SizedBox or ConstrainedBox
ConstrainedBox(
constraints: BoxConstraints(maxHeight: 100.0),
child: AlertDialog(
...
),
);
Alternatively, you can set shrinkWrap to true in your ListView so that it takes the least amount of vertical space necessary.
ListView(
shrinkWrap: true,
...
)
A Dialog's insetPadding property controls the padding around the dialog so you could set values to effectively reduce the height / width of the dialog, e.g.
showDialog(
context: context,
builder: (context) {
return AlertDialog(
insetPadding: EdgeInsets.symmetric(
horizontal: 50.0,
vertical: 100.0,
),
title: Text('...'),
content: Text('...'),
);
});

How can move content bottom of AlertDialog in Flutter

I want to display some option in the bottom of Flutter AlertDialog. As you can see
If I understand correctly you can use the showDialog component which lays behind the AlertDialog with a nested Column. For a greyed out background ou can use Opacity with a Container:
showDialog(
context: context,
builder: (context) {
return Opacity(
opacity: 0.8, // some opacity
child: Container(
// container to set color
color: Colors.grey,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: [
RaisedButton(
onPressed: null,
), // replace with your buttons
RaisedButton(
onPressed: null,
),
]
)
)
);
}
);
Simple way: you can use padding
showDialog<String>(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top:300.0),
child: //AlertDiloag / CupertinoAlertDialog
);
},
);