How to set showModalBottomSheet to initally half height but expandable and dismissable - flutter

Following code is dismissable but it takes up full height initially due to colorsList having a lot of colors:
onPressed: () {
showModalBottomSheet(
context: this.context,
isScrollControlled: true,
builder: (BuildContext context) {
return Wrap(
children: [
for (List<Color> colors in colorsList)
Container(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("${colorsName[colorsList.indexOf(colors)]}"),
Container(
height: 80,
child: ListView.builder(
padding: EdgeInsets.only(left: 8, right: 8,),
scrollDirection: Axis.horizontal,
itemCount: colors.length,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.only(left: 8,),
child: FloatingActionButton(
backgroundColor: colors[index],
tooltip: "Choose this color",
onPressed: () {
setState(() {
this.color = colors[index];
});
Navigator.pop(context);
},
),
);
},
),
),
],
),
),
],
);
}
);
},

wrap the content of the sheet by a stream builder ,and update the stream when drag occurs
StreamController<double> controller = StreamController.broadcast();
RaisedButton(
child: Text('Show Buttom Sheet'),
onPressed: () {
showModalBottomSheet(context: context, builder: (context){
return StreamBuilder(
stream: controller.stream,
builder:(context,snapshot) => GestureDetector(
onVerticalDragUpdate: (DragUpdateDetails details){
position = MediaQuery.of(context).size.height- details.globalPosition.dy;
print('position dy = ${position}');
position.isNegative?Navigator.pop(context)
:controller.add(position);
},
behavior: HitTestBehavior.translucent,
child:
Container(
color: Colors.red,
height: snapshot.hasData ? snapshot.data:200.0,
width: double.infinity,
child: Text('Child'),
)),
);
});
}),

Related

Make a dynamic Listview inside a ListView =

as of the picture down below, I would like to make listview, where it is possible to add more lines(red) under each listview card.
I have implemented the overall listview(green), with the button that should add a list inside the list. Code is at the bottom
The picture is taken from the Strong app
My design right now is as follows:
Expanded(
// ignore: unnecessary_new
child: new ListView.builder(
itemCount: litems.length,
itemBuilder: (BuildContext ctxt, int Index) {
return Card(
child: Padding(
padding: EdgeInsets.all(10),
child: ExpansionTile(
initiallyExpanded: true,
title: Text(
litems[Index],
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
children: <Widget>[
ElevatedButton(
onPressed: () {
litems.add('hei');
setState(() {});
},
child: const Text('Add Set')),
SizedBox(height: 5),
],
leading: IconButton(
icon: const Icon(
Icons.close,
color: Colors.red,
),
onPressed: () {
litems.removeAt(Index);
setState(() {});
},
),
)));
})),
ElevatedButton(
onPressed: () {
litems.add('hei');
setState(() {});
},
child: const Text('Add Exercises')),
Try my code:
List<List<String>> parent = [];//init Parent
//Parent(Exercises) layout
Column(
children: [
ListView.builder(
itemCount: parent.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return _buildList(parent[index]);
}),
TextButton(
onPressed: () {
parent.add([]);
setState(() {});
},
child: Text("Add Parent"))
],
)
//build children
_buildList(List<String> list) {
return Column(
children: [
ListView.builder(
itemCount: list.length,
shrinkWrap: true,
padding: EdgeInsets.all(0),
physics: const NeverScrollableScrollPhysics(),
itemExtent: 50,
itemBuilder: (context, index) {
return Container(
color: Colors.red.withOpacity((index * 5) / 100),
margin: EdgeInsets.symmetric(vertical: 0),
child: Text('Item'),
);
},
),
TextButton(
onPressed: () {
list.add("value");
setState(() {});
},
child: Text("Add Item"))
],
);
}

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(....);
})

Scroll horizontally in ListView builder within a Future builder - Flutter

I am trying to create a horizontally scrollable list of cards but it breaks each. I am just not sure where I am going wrong. I tried adding Expandable in several places and I still get errors.
I am hoping this is resolvable or do I need to separate each element within the Container/Column (2nd one).
It is definitely some sort of sizing I am missing but just not able to pinpoint where.
body: SafeArea(
child: Column(
children: [
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(...),
Padding(...),
Padding(...),
SizedBox(...),
Padding(...),
SizedBox(...),
Padding(...),
SizedBox(...),
Container(
height: 200,
color: Colors.blue[200],
child: ListView(
shrinkWrap: true,
physics: ScrollPhysics(),
scrollDirection: Axis.vertical,
children: [
FutureBuilder(
future: getAudioList(),
builder: (context, data) {
if (data.hasError) {
return Center(child: Text("${data.error}"));
} else if (data.hasData) {
var items = data.data as List<MyOtherFunc>;
var categories;
//Split into categories
if (items != null) {
categories = groupBy(
items, (MyOtherFunc ma) => ma.category);
Map<String, List<MindfulAudio>> catMap =
categories;
return new ListView.builder(
padding:
const EdgeInsets.fromLTRB(8, 0, 8, 0),
itemCount: catMap.length,
physics:
const AlwaysScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (context, index) {
String key = catMap.keys.elementAt(index);
return Card(
child: ListTile(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
MyFunc(
catMap[key], key),
),
);
},
leading: Icon(icons[index],
size: 25, color: colour[index]),
title: new Text("$key"),
trailing: new IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
MyFunc(
catMap[key], key),
),
);
},
icon: Icon(
FontAwesomeIcons.chevronRight,
color: PINK,
)),
),
);
},
);
}
return Center(
child: CircularProgressIndicator(),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
],
),
),
],
),
),
],
),
),
Try this layout
SizedBox(
height: MediaQuery.of(context).size.height,
width : MediaQuery.of(context).size.width,
child: SingleChildScrollView (
child: Column(
children: [
//Other widgets which will scroll vertically in the column
SingleChildScrollView (
scrollDirection: Axis.horizontal,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
//Items you wish to scroll horizontally
]
)
),
//Some more widgets if you wish to add below the horizontal scroll
]
)
)
)

How To Add TabBar with Horizontal And Vertical ListView In One Tab Scrolling In Respective Directions

Flutter newbie here if my code looks too messy. Managed to figure out a few basic layouts and have implemented a TabBar. In 2nd Tab(COMICS). I have a horizontal ListView and a vertical one. I just can't figure out how to make the horizontal ListView scroll without changing tabs.
How i create the tabs:
return NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
title: Text('NestedScrollView'),
)
];
},
body: Scaffold(
appBar: AppBar(
toolbarHeight: 40,
centerTitle: true,
backgroundColor: Color(0xFFb92136),
title: Text('Kata Kata African Cartoons'),
// actions: [
// IconButton(onPressed: () {}, icon: Icon(Icons.search)),
// IconButton(onPressed: () {}, icon: Icon(Icons.more_vert))
// ],
bottom: TabBar(
controller: tabController,
tabs: [
Tab(
text: "VIDEOS",
),
Tab(
text: "COMICS",
),
Tab(
text: "MAGAZINE",
),
],
),
),
body: TabBarView(
controller: tabController,
children: [
VideosTab(),
ComicsTab(),
Magazines(),
],
),
));
Main body of comics tab
return Column(children: [
Container(child: Card(
elevation: 20,
child: Column(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: EdgeInsets.all(5),
child: Text("Katakata Long Comics"))
]),
Card(
child: StreamBuilder(
stream: ref.onValue,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData &&
!snapshot.hasError &&
snapshot.data.snapshot.value != null) {
lists.clear();
DataSnapshot dataValues = snapshot.data.snapshot;
Map<dynamic, dynamic> values = dataValues.value as Map;
values.forEach((key, values) {
lists.add(values);
});
return Container(
height: 200,
child: new ListView.builder(
physics: const NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: lists.length,
itemBuilder: (BuildContext context, int index) {
return Card(
margin: EdgeInsets.fromLTRB(2, 2, 2, 2),
elevation: 20,
child: GestureDetector(
onTap: () {
String pdfurl = lists[index]["image"];
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PDFViewer(
url: pdfurl,
),
));
},
child: Padding(
padding: EdgeInsets.all(5),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Column(
children: [
Container(
height: 100,
width: 100,
child: Image(
image:
NetworkImage(lists[index]["image"]),
),
),
Text(lists[index]["name"]),
],
),
],
),
),
),
);
},
),
);
}
return Container(child: Text("Add Plants"));
},
),
),
],
),
)),
Container(child: Card(
elevation: 20,
child: Column(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: EdgeInsets.all(5),
child: Text("Continuous Humour"))
]),
Card(
child: StreamBuilder(
stream: secref.onValue,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData &&
!snapshot.hasError &&
snapshot.data.snapshot.value != null) {
seclists.clear();
DataSnapshot dataValues = snapshot.data.snapshot;
Map<dynamic, dynamic> values = dataValues.value as Map;
values.forEach((key, values) {
seclists.add(values);
});
return new ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: seclists.length,
itemBuilder: (BuildContext context, int index) {
return Card(
margin: EdgeInsets.fromLTRB(2, 2, 2, 2),
elevation: 20,
child: GestureDetector(
onTap: () {
String pdfurl = seclists[index]["image"];
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PDFViewer(url: pdfurl,
),
));
},
child: Padding(
padding: EdgeInsets.all(5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
height: 200,
width: 400,
child: Image(
image:
NetworkImage(seclists[index]["image"]),
),
),
Text(seclists[index]["name"]),
],
),
),
),
);
},
);
}
return Container(child: Text("Add Plants"));
},
),
),
],
),
)),
]);
You have NeverScrollableScrollPhysics() defined as the physics for your listviews. That means they won't scroll even when there is no tabview around them. Also they won't absorb the scroll event and defer them to the tabview.

How to change place of children widgets in Column?

I need to change the place of the widget in my UI. But I could not achieve it.
I tried this code But it does not work for me. If someone knows how to do it please help.
Change Place of BankCard Widget to Another BankCard Widget, or change the place of any widgets in a Column.
Below you can find code that I tried:
Widget nonNullBody(List<GetConversionCards> cards) {
_column=Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"fromSum".tr(),
style: TextStyle(
fontSize: 16, fontWeight: FontWeight.w700),
textAlign: TextAlign.left,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: BankCard(
card: Bank.CreditCardModel.fromUzCard(
cards[bloc.selectedIndex.value])),
),
IconButton(
icon: Icon(Icons.sync),
onPressed: () {
_key.currentState.setState(() {
Widget t=_column.children[0];
_column.children[0]=_column.children[3];
_column.children[3]=t;
});
},
),
Text("toVisa".tr(),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700),
textAlign: TextAlign.left),
Padding(
padding: const EdgeInsets.all(8.0),
child: BankCard(
card: Bank.CreditCardModel.fromVisaCard(
cards[bloc.selectedIndex.value])),
),
],);
bloc.tokenCardUzs = cards[0].uzsCardId;
final size = MediaQuery.of(context).size;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: SingleChildScrollView(
child: Container(
height: size.height - appBar().preferredSize.height + 5,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
height: 30,
child: ValueListenableBuilder(
valueListenable: bloc.selectedIndex,
builder: (context, v, w) {
return ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: cards.length,
itemBuilder: (context, index) {
return Container(
width: 8.0,
height: 8.0,
margin: EdgeInsets.symmetric(
vertical: 12.0, horizontal: 2.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: bloc.selectedIndex.value == index
? Color.fromRGBO(0, 0, 0, 0.9)
: Color.fromRGBO(0, 0, 0, 0.4)),
);
});
}),
),
Flexible(
fit: FlexFit.loose,
flex: 5,
child: PageView.builder(
scrollDirection: Axis.horizontal,
itemCount: cards.length,
onPageChanged: (_) {
bloc.tokenCardUzs = cards[_].uzsCardId;
bloc.selectedIndex.value = _;
},
itemBuilder: (BuildContext context, int index) {
return StatefulBuilder(
key: _key,
builder: (BuildContext context,
void Function(void Function()) state) {
return _column; );
}),
),
Flexible(
child: TextFieldWidget(
controller: bloc.amountFieldController,
iconData: Icons.monetization_on,
validator: null,
hintText: "enterAmount".tr(),
labelText: "dollarCurrency".tr(),
),
),
ConfirmButton(
text: 'next'.tr(),
onPressed: () {
showPopUp(context, () async {
Navigator.of(context, rootNavigator: true).pop();
waitTransactionWidget(context);
int usd =
(double.parse(bloc.amountFieldController.text) * 100)
.toInt();
bool result = await Repository.getInstance()
.convUzsUsd(bloc.tokenCardUzs, usd);
print("result conv $result");
Navigator.of(context, rootNavigator: true).pop();
Navigator.pushReplacement(
context,
PageTransition(
child: MyHomePage(),
type: PageTransitionType.fade));
},
title: Text("wouldYouLikeToExchange".tr()),
subtitle: Text("${bloc.amountFieldController.text} " +
"dollarCurrency".tr()));
},
),
],
),
),
),
);
}
I recommend you create a list that contains widgets then assign it to the childrens parameters of Column. So when you change something about the list it will change the column as well.
like this :
List<Widget> list = new List();
#override
Widget build(BuildContext context) {
Column(
children: list,
);
}
adjustWidgetList() {
setState(() {
list.add(Text("lorem ipsum"));
list.add(Text("dolar sit amet."));
.
.
.
list.remove(0);
});
}
But don't forget to make your changes inside of setState.