listview can not scrolling down inside sliding_up_panel flutter - flutter

I have used panelBuilder to add a ListView and inside ListView, I have used ListView to show my list but when I scrolled down the inside ListView, panel also scrolls down. This is my code.
SlidingUpPanel(
controller: _panelController,
maxHeight: _panelHeightOpen,
minHeight: _panelHeightClosed,
parallaxEnabled: true,
parallaxOffset: .5,
borderRadius: const BorderRadius.only(topLeft: Radius.circular(30.0), topRight: Radius.circular(30.0)),
color: const Color(0xFFD6EDFF),
// Maps
body: Padding(
padding: EdgeInsets.only(bottom: _paddingBottomMap),
child: GoogleMap()
),
panelBuilder: (controller) {
return Builder(builder: (BuildContext context) {
return ListView(padding: EdgeInsets.zero, controller: controller,
children: <Widget>[
//My List
Container(
margin: const EdgeInsets.symmetric(horizontal: 20),
child: const Text("My Favorite")
),
const SizedBox(height: 10),
Container(
height: _panelHeightOpen - 370,
width: double.maxFinite,
child: ListView.builder(
controller: _listFavContronller,
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: _favLocation.length,
itemBuilder: (context, index) {
return Container(
ListTile(
onTap: () async {
_panelController.close();
await goToCurrentLocation();
},
title: Text("${_favLocation[index].locationName}")
)
);
}
)
)
]
)
}
},
onPanelSlide: (position) async {
setState(() {});
},
onPanelClosed: () {
FocusScope.of(context).requestFocus(FocusNode());
},
))
How do I fix this problem to panel will not scroll down when users scroll the inside ListView down?
Here is Video : https://drive.google.com/file/d/1mOEns6ie3KqRmGXQRenJT560brNjlQni/view?usp=sharing

In My Case it's working fine for me:
I Have use static itemCount:200
SlidingUpPanel(
controller: _panelController,
maxHeight: _panelHeightOpen,
minHeight: _panelHeightClosed,
parallaxEnabled: true,
parallaxOffset: .5,
borderRadius: const BorderRadius.only(topLeft: Radius.circular(30.0), topRight: Radius.circular(30.0)),
color: const Color(0xFFD6EDFF),
// Maps
body: Padding(
padding: EdgeInsets.only(bottom: 10),
// child: GoogleMap()
),
panelBuilder: (controller) {
return Builder(builder: (BuildContext context) {
return ListView(padding: EdgeInsets.zero, controller: controller,
children: <Widget>[
//My List
Container(
color: Colors.yellow,
margin: const EdgeInsets.symmetric(horizontal: 20),
child: const Text("My Favorite")
),
const SizedBox(height: 10),
Container(
color: Colors.blue,
height: _panelHeightOpen - 370,
width: double.maxFinite,
child: ListView.builder(
controller: _listFavContronller,
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount:200,
// itemCount: _favLocation.length,
itemBuilder: (context, index) {
return Container(
color: Colors.red,
child: ListTile(
onTap: () async {
_panelController?.close();
// await goToCurrentLocation();
},
// title: Text("${_favLocation[index].locationName}")
title: Text("Printed"),
)
);
}
)
)
]
);
}
);
},
onPanelSlide: (position) async {
setState(() {});
},
onPanelClosed: () {
FocusScope.of(context).requestFocus(FocusNode());
},
)

Related

how to scroll search widget along with the list view result flutter

How to scroll the search widget along with the list view result flutter whenever I use singlechildscrollview it gives an error.Help me to solve the issue.
How to scroll the search widget along with the list view result flutter whenever I use singlechildscrollview it gives an error.Help me to solve the issue
import 'package:coronavirus_app/Services/states_services.dart';
import 'package:coronavirus_app/resources/sizeconfig.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shimmer/shimmer.dart';
import '../Modal/world_countries_modal.dart';
import '../resources/colormanager.dart';
import '../resources/routesmanager.dart';
class CountriesListScreen extends ConsumerStatefulWidget {
const CountriesListScreen({Key? key}) : super(key: key);
#override
ConsumerState<CountriesListScreen> createState() =>
_CountriesListScreenState();
}
class _CountriesListScreenState extends ConsumerState<CountriesListScreen> {
final TextEditingController searchcontroller = TextEditingController();
#override
Widget build(BuildContext context) {
SizeConfig().init(context);
StatesServices services = StatesServices();
final countrydata = ref.watch(countrydataProvider);
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
),
body: countrydata.when(
error: (err, stack) => Text('Error: ${err.toString()}'),
loading: () => Shimmer.fromColors(
baseColor: ColorManager.grey,
highlightColor: ColorManager.lightGrey,
child: ListView.builder(
itemCount: 5,
itemBuilder: ((context, index) {
return Column(
children: [
ListTile(
title: Container(
height: getProportionateScreenHeight(20),
width: getProportionateScreenWidth(89),
color: ColorManager.white,
),
leading: SizedBox(
height: getProportionateScreenHeight(100),
width: getProportionateScreenWidth(50),
child: Container(
color: ColorManager.white,
)),
subtitle: Container(
height: getProportionateScreenHeight(20),
width: getProportionateScreenWidth(89),
color: ColorManager.white,
),
)
],
);
})),
),
data: ((data) {
List<WorldCountriesModal> countrieslist =
data.map((e) => e).toList();
return Column(children: [
SizedBox(
height: getProportionateScreenHeight(50),
width: double.infinity,
child: TextFormField(
controller: searchcontroller,
onChanged: (value) {
setState(() {});
},
decoration: InputDecoration(
contentPadding:
const EdgeInsets.symmetric(vertical: 10),
hintText: 'Search with country name',
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(50))),
),
),
Expanded(
child: ListView.builder(
itemCount: countrieslist.length,
itemBuilder: ((context, index) {
var data = countrieslist[index];
var countrydata = countrieslist[index].country;
if (searchcontroller.text.isEmpty) {
return Column(
children: [
InkWell(
onTap: (() {
FocusScope.of(context).unfocus();
Navigator.pushNamed(context, Routes.detail,
arguments: data);
}),
child: ListTile(
title: Text(data.country.toString()),
leading: SizedBox(
height: getProportionateScreenHeight(100),
width: getProportionateScreenWidth(50),
child: Image(
image: Image.network(data
.countryInfo!.flag
.toString())
.image),
),
subtitle: Text(data.cases.toString()),
),
)
],
);
} else if (countrydata!
.toLowerCase()
.contains(searchcontroller.text.toLowerCase())) {
return Column(
children: [
InkWell(
onTap: (() {
FocusScope.of(context).unfocus();
Navigator.pushNamed(context, Routes.detail,
arguments: data);
}),
child: ListTile(
title: Text(data.country.toString()),
leading: SizedBox(
height: getProportionateScreenHeight(100),
width: getProportionateScreenWidth(50),
child: Image(
image: Image.network(data
.countryInfo!.flag
.toString())
.image),
),
subtitle: Text(data.cases.toString()),
),
)
],
);
} else {
return Container(
height: 0,
);
}
})),
),
]);
})
}
}
ListView.builder(
shrinkWrap: true,
primary: false
Hey If you want to use singlechildscrollview then you need to assign
ListView.builder(
shrinkWrap: true
to your ListView.builder.
Because you can't assign the 2 scroll views to your screen at a time.
Put your widgets in SingleChildScrollView widget as the following example:
return Scaffold(
appBar: AppBar( ..........) ,
body: Container(
margin: const EdgeInsets.only(top: 15, right: 10),
alignment: Alignment.topRight,
width: double.infinity,
height: double.infinity,
==> child:SingleChildScrollView(
scrollDirection: Axis.vertical,
child: .......

List view childs get out of container

I am trying to make a list view only occupy part of the screen, but it keeps growing till the end not respecting the contianer constraints. I tried to use a sizedbox too but it didn' work. List tiles outside the container are shown without any widget inside, but the background is shown anyways
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: pedidos,
builder: (context, AsyncSnapshot<List<Pedido>> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.6,
child: ListView.builder(
itemCount: snapshot.data!.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Hero(
tag:
"pedidos_card${snapshot.data![index].idPedido}",
child: ListTile(
tileColor: Colors.white,
leading: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle),
child: Center(
child: Text(
style: Theme.of(context)
.textTheme
.headlineSmall,
"${snapshot.data![index].idPedido}"),
),
),
title: Text(
'Pedido: ${snapshot.data![index].idPedido}'),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 10),
Text(
'Estado: ${snapshot.data![index].estadoPedido.last.tipoEstadoPedido.name}'),
SizedBox(height: 10),
Text(
"Cliente: ${snapshot.data![index].cliente.nombre}")
],
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
trailing: Checkbox(
value: pedidosSeleccion
.contains(snapshot.data![index]),
onChanged: (bool? value) {
// value = checkboxList[index];
// setState(() {});
},
),
onTap: () {
bool isSelected = pedidosSeleccion
.contains(snapshot.data![index]);
if (isSelected) {
pedidosSeleccion
.remove(snapshot.data![index]);
} else {
pedidosSeleccion.add(snapshot.data![index]);
}
setState(() {});
},
),
));
}),
),
ElevatedButton(
onPressed: () {}, child: Text('Ver ultima milla')),
],
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
});
}
}
example
you can use Expanded instead of Sizedbox
eg:-
Column(
children:[
Expanded(flex:9,
child: ListView(
padding: const EdgeInsets.only(top: 10.0),
children: snapshot.map((data) => _buildListItem(context, data)).toList(),
),
),
Expanded(flex:1,
child:
ElevatedButton(
// fill in required params
),
)
])

Unable to multi-select check boxes in bottom sheet

I am trying to list out a few texts after a bottom sheet opens, this is dynamic and comes from an API. Once the bottom sheet function is triggered, the API is called and the list view updates. In this list view I used CheckboxListTile, the problem is I am not able to do multiple selections (nor single select) on the checkboxes.
This is what I have so far:
var selectedIndex = [];
The above code is in the _MainScreenState and the function for the bottom screen is triggered in one of the buttons as:
...
onPressed: () {
_showModalBottomSheet(context).then((value) => setState(() {
index = value;
}));
}
...
Bottom sheet code
Future<AllApps?> _showModalBottomSheet(BuildContext context) {
return showModalBottomSheet<AllApps>(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
clipBehavior: Clip.antiAliasWithSaveLayer,
context: context,
isScrollControlled: true,
builder: (context) {
return FractionallySizedBox(
heightFactor: 0.9,
child: Column(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(150.0, 20.0, 150.0, 20.0),
child: Container(
height: 8.0,
width: 80.0,
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(20.0),
),
),
),
FutureBuilder<AllApps>(
future: getAllApps(), // <- API call
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
shrinkWrap: true,
physics: const ScrollPhysics(),
itemCount: snapshot.data?.data.length,
itemBuilder: (context, index) {
final app = snapshot.data?.data[index];
return CheckboxListTile(
enableFeedback: true,
title: Text(app!.name),
value: selectedIndex.contains(app.id),
onChanged: (_) {
if (selectedIndex.contains(app.id)) {
setState(() {
selectedIndex.remove(app.id);
});
} else {
setState(() {
selectedIndex.add(app.id);
});
}
},
);
},
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return const Center(child: CircularProgressIndicator());
},
),
Padding(
padding: const EdgeInsets.only(top: 20.0, right: 5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: ElevatedButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.pop(context);
},
),
),
ElevatedButton(
child: const Text('Save'),
onPressed: () {
Navigator.pop(context);
},
),
],
),
)
],
),
);
});
}
I am able to see the lists being built but I am not able to select any one of them (gif screenshot):
How should I enable multiple selections?
In order to apply changes in the state to the modal, use StateFulBuilder:
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) {
return StatefulBuilder( // this is new
builder: (BuildContext context, StateSetter setState) {
return FractionallySizedBox(
heightFactor: 0.9,
child: Column(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(150.0, 20.0, 150.0, 20.0),
child: Container(
height: 8.0,
width: 80.0,
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(20.0),
),
),
),
FutureBuilder<AllApps>(
future: getAllApps(), // <- API call
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
shrinkWrap: true,
physics: const ScrollPhysics(),
itemCount: snapshot.data?.data.length,
itemBuilder: (context, index) {
final app = snapshot.data?.data[index];
return CheckboxListTile(
enableFeedback: true,
title: Text(app!.name),
value: selectedIndex.contains(app.id),
onChanged: (_) {
if (selectedIndex.contains(app.id)) {
setState(() {
selectedIndex.remove(app.id);
});
} else {
setState(() {
selectedIndex.add(app.id);
});
}
},
);
},
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return const Center(child: CircularProgressIndicator());
},
),
Padding(
padding: const EdgeInsets.only(top: 20.0, right: 5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: ElevatedButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.pop(context);
},
),
),
ElevatedButton(
child: const Text('Save'),
onPressed: () {
Navigator.pop(context);
},
),
],
),
)
],
),
);
});
});
You can use StatefulBuilder for providing setState in bottom sheet. Try as follows:
return StatefulBuilder(
builder:(BuildContext context,setState){
return FractionallySizedBox(....);
})

How can I keep the two images in one screen?

How can I split the screen into two different parts. I'm learning filters and I made this code
I want to add images from the server
When I put picture number 1 or 2 for the first time, there is no problem
But when choosing the second image, the first image is deleted. I think because it refreshed the page (rebuild)
How can I keep the two images in one screen? What is the best way?
Thank you
ٍ Split the screen into two parts
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:lifetb/constants/my_colors.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
class BeforeAfter2 extends StatefulWidget {
const BeforeAfter2({
Key? key,
this.imageSelected1,
this.imageSelected2,
}) : super(key: key);
final imageSelected1;
final imageSelected2;
#override
_BeforeAfter2State createState() => _BeforeAfter2State();
}
/// change it to Cata
var imageSelected1;
var imageSelected2;
var imageBody = true;
var id;
var username;
var email;
getPref() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
username = preferences.getString("username");
id = preferences.getString("id");
email = preferences.getString("email");
if ( id == null) {
print("empty");
}
}
Future getData() async {
getPref();
if ( id == null) {
print("empty 2");
}
var url = "http://10.0.2.2/641984.php";
var data = {"user_id": "$id"};
var response = await http.post(Uri.parse(url), body: data);
var responsebody = jsonDecode(response.body);
return responsebody;
}
class _BeforeAfter2State extends State<BeforeAfter2> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Column(
children: [
Expanded(
flex: 1,
child: SizedBox(
height: double.infinity,
width: double.infinity,
child: widget.imageSelected1 == null
? addImage(true,true)
: showCata()
),
),
Expanded(
flex: 1,
child: SizedBox(
height: double.infinity,
width: double.infinity,
child: widget.imageSelected2 == null
? addImage(false,true)
: showCata1()
),
),
],
),
));
}
}
addImage(addText, goToAdd) {
return Column(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
addText ? Text("Add 1 Photo") : Text("Add 2 Photo"),
SizedBox(height: 26),
Container(
alignment: Alignment.center,
child: FloatingActionButton(
heroTag: null,
child: Icon(
Icons.add,
color: white,
size: 45,
),
onPressed: () {
goToAdd
? showCata()
: showCata1();
},
)),
],
),
],
);
}
showCata(){
return FutureBuilder(
future: getData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Loading...'),
Center(child: CircularProgressIndicator())
],
);
} else if (snapshot.data.isEmpty) {
return Padding(
padding: EdgeInsets.all(15),
child: Text("You don't Have any Photo")
);
}
return GridView.builder(
// shrinkWrap: true,
itemCount: snapshot.data.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
// crossAxisSpacing: 10
),
itemBuilder: (context, i) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: InkWell(
onTap: () {
Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: (context) {
return BeforeAfter2(
imageSelected1: snapshot.data[i]["post_image"],
);
}));
},
child: Container(
margin: EdgeInsets.only(top: 10, left: 10),
decoration: BoxDecoration(
border: Border.all(
color: Colors.deepPurple.shade500,
width: 2),
borderRadius:
BorderRadius.all(Radius.circular(35)),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
"http://10.0.2.2/upload/" +
snapshot.data[i]["post_image"]),
)),
),
),
);
},
);
},
);
}
showCata1(){
return FutureBuilder(
future: getData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Loading...'),
Center(child: CircularProgressIndicator())
],
);
} else if (snapshot.data.isEmpty) {
return Padding(
padding: EdgeInsets.all(15),
child: Text("You don't Have any Photo")
);
}
return GridView.builder(
// shrinkWrap: true,
itemCount: snapshot.data.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
// crossAxisSpacing: 10
),
itemBuilder: (context, i) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: InkWell(
onTap: () {
Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: (context) {
return BeforeAfter2(
imageSelected2: snapshot.data[i]["post_image"],
);
}));
},
child: Container(
margin: EdgeInsets.only(top: 10, left: 10),
decoration: BoxDecoration(
border: Border.all(
color: Colors.deepPurple.shade500,
width: 2),
borderRadius:
BorderRadius.all(Radius.circular(35)),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
"http://10.0.2.2/upload/" +
snapshot.data[i]["post_image"]),
)),
),
),
);
},
);
},
);
}
remove SizedBox()s and change SafeArea with child
class _BeforeAfter2State extends State<BeforeAfter2> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Column(
children: [
Expanded(
flex: 1,
child: widget.imageSelected1 == null
? addImage(true,true)
: showCata()
),
Expanded(
flex: 1,
child: widget.imageSelected2 == null
? addImage(false,true)
: showCata1()
),
],
),
));
}
}
addImage(addText, goToAdd) {
return Column(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
addText ? Text("Add 1 Photo") : Text("Add 2 Photo"),
SizedBox(height: 26),
Container(
alignment: Alignment.center,
child: FloatingActionButton(
heroTag: null,
child: Icon(
Icons.add,
color: white,
size: 45,
),
onPressed: () {
goToAdd
? showCata()
: showCata1();
},
)),
],
),
],
);
}
showCata(){
return FutureBuilder(
future: getData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Loading...'),
Center(child: CircularProgressIndicator())
],
);
} else if (snapshot.data.isEmpty) {
return Padding(
padding: EdgeInsets.all(15),
child: Text("You don't Have any Photo")
);
}
return GridView.builder(
// shrinkWrap: true,
itemCount: snapshot.data.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
// crossAxisSpacing: 10
),
itemBuilder: (context, i) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: InkWell(
onTap: () {
Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: (context) {
return BeforeAfter2(
imageSelected1: snapshot.data[i]["post_image"],
);
}));
},
child: Container(
margin: EdgeInsets.only(top: 10, left: 10),
decoration: BoxDecoration(
border: Border.all(
color: Colors.deepPurple.shade500,
width: 2),
borderRadius:
BorderRadius.all(Radius.circular(35)),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
"http://10.0.2.2/upload/" +
snapshot.data[i]["post_image"]),
)),
),
),
);
},
);
},
);
}
showCata1(){
return FutureBuilder(
future: getData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Loading...'),
Center(child: CircularProgressIndicator())
],
);
} else if (snapshot.data.isEmpty) {
return Padding(
padding: EdgeInsets.all(15),
child: Text("You don't Have any Photo")
);
}
return GridView.builder(
// shrinkWrap: true,
itemCount: snapshot.data.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
// crossAxisSpacing: 10
),
itemBuilder: (context, i) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: InkWell(
onTap: () {
Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: (context) {
return BeforeAfter2(
imageSelected2: snapshot.data[i]["post_image"],
);
}));
},
child: Container(
margin: EdgeInsets.only(top: 10, left: 10),
decoration: BoxDecoration(
border: Border.all(
color: Colors.deepPurple.shade500,
width: 2),
borderRadius:
BorderRadius.all(Radius.circular(35)),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
"http://10.0.2.2/upload/" +
snapshot.data[i]["post_image"]),
)),
),
),
);
},
);
},
);
}

Flutter custom bottom sheet

I have a ListView where I want to implement a nice way to delete list items using a bottom sheet with actions on. Initially I went down the path of simply calling showBottomSheet() in the onLongPress event handler for my list items, which would successfully open a bottom sheet with my action buttons on. However, this would automatically add a back button to the AppBar which is not what I want.
I then went down the route of trying out animations, such as SlideTransition and AnimatedPositioned:
class FoldersListWidget extends StatefulWidget {
#override
_FoldersListWidgetState createState() => _FoldersListWidgetState();
}
class _FoldersListWidgetState extends State<FoldersListWidget>
with SingleTickerProviderStateMixin {
double _bottomPosition = -70;
#override
Widget build(BuildContext context) {
return Stack(
children: [
FutureBuilder<List<FolderModel>>(
future: Provider.of<FoldersProvider>(context).getFolders(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, i) {
final folder = snapshot.data[i];
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 10),
elevation: 1,
child: ListTile(
title: Text(folder.folderName),
leading: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 50,
child: Consumer<FoldersProvider>(
builder:
(BuildContext context, value, Widget child) {
return value.deleteFolderMode
? CircularCheckBox(
value: false,
onChanged: (value) {},
)
: Icon(
Icons.folder,
color: Theme.of(context).accentColor,
);
},
),
),
],
),
subtitle: folder.numberOfLists != 1
? Text('${folder.numberOfLists} items')
: Text('${folder.numberOfLists} item'),
onTap: () {},
onLongPress: () {
Provider.of<FoldersProvider>(context, listen: false)
.toggleDeleteFolderMode(true); // removes fab from screen
setState(() {
_bottomPosition = 0;
});
},
),
);
},
);
},
),
AnimatedPositioned(
bottom: _bottomPosition,
duration: Duration(milliseconds: 100),
child: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(25),
topRight: Radius.circular(25),
),
child: Container(
height: 70,
width: MediaQuery.of(context).size.width,
color: Theme.of(context).colorScheme.surface,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Expanded(
child: IconAboveTextButton(
icon: Icon(Icons.cancel),
text: 'Cancel',
textColour: Colors.black,
opacity: 0.65,
onTap: () => setState(() {
_bottomPosition = -70;
}),
),
),
VerticalDivider(
color: Colors.black26,
),
Expanded(
child: IconAboveTextButton(
icon: Icon(Icons.delete),
text: 'Delete',
textColour: Colors.black,
opacity: 0.65,
),
),
],
),
),
),
),
],
);
}
}
This slides the bottom Container on and off the screen but my issue is that it covers the last list item:
Could anyone suggest a better way of doing this or simply a way to adjust the height of the ListView so that when the Container slides up, the ListView also slides up.
Wrap ListView.builder inside a container and set its bottom padding as (70+16)
70 (height of bottom sheet), 16 (some default padding to make it look better if you like).
return Container(
padding: EdgetInset.ony(bottom: (70+16)),
child:ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, i) {
.....
.....