Reputation: 8993
This code is very simple: shows a modal bottom sheet and when the uses clicks the button, it increases the height of the sheet by 10.
But nothing happens. Actually, it only updates its size if the user "slides" the bottom sheet with it's finger (I belive that swipe causes a internal setState on the sheet).
My question is: how do I call the update state of a ModalBottomSheet?
showModalBottomSheet(
context: context,
builder: (context) {
return Container(
height: heightOfModalBottomSheet,
child: RaisedButton(
onPressed: () {
setState(() {
heightOfModalBottomSheet += 10;
});
}),
);
});
Upvotes: 106
Views: 98900
Reputation: 1271
create a separate StatefulWidget for the showModalBottomSheet(), like
showModalBottomSheet(
context: context,
builder: (ctx) {
return MapBottomSheet();
});
Bottom Sheet Statefulwidget
class MapBottomSheet extends StatefulWidget {
@override
_MapBottomSheetState createState() => _MapBottomSheetState();
}
class _MapBottomSheetState extends State<MapBottomSheet> {
List<String> places = [];
void _setPlaces(String place) {
setState(() {
places.add(place);
});
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.black12,
child: Column(
children: [
AppTextField(
hint: "Search",
onEditingComplete: () {},
onChanged: (String text) {},
onSubmitted: (String text) async {
// Await the http get response, then decode the json-formatted response.
var response = await http.get(Uri.parse(
'https://api.mapbox.com/geocoding/v5/mapbox.places/$text.json?access_token=pk.eyJ1IjoidjNyc2lvbjkiLCJhIjoiY2ttNnZldmk1MHM2ODJxanh1ZHZqa2I3ZCJ9.e8pZsg87rHx9FSM0pDDtlA&country=PK&fuzzyMatch=false&place=park'));
if (response.statusCode == 200) {
Map<String, dynamic> data = jsonDecode(response.body);
print(data.toString());
List<dynamic> features = data['features'];
features.forEach((dynamic feature) {
setState(() {
_setPlaces(feature['place_name']);
});
});
} else {
print('Request failed with status: ${response.statusCode}.');
}
},
),
Expanded(
child: Container(
height: 250.0,
width: double.infinity,
child: ListView.builder(
itemCount: places.length,
itemBuilder: (ctx, idx) {
return Container(
child: Text(places[idx]),
);
}),
),
),
],
),
);
}
}
Upvotes: 9
Reputation: 268384
Screenshot:
Create a class:
class MyBottomSheet extends StatefulWidget {
@override
_MyBottomSheetState createState() => _MyBottomSheetState();
}
class _MyBottomSheetState extends State<MyBottomSheet> {
bool _flag = false;
@override
Widget build(BuildContext context) {
return Column(
children: [
FlutterLogo(
size: 300,
style: FlutterLogoStyle.stacked,
textColor: _flag ? Colors.black : Colors.red,
),
RaisedButton(
onPressed: () => setState(() => _flag = !_flag),
child: Text('Change Color'),
)
],
);
}
}
Usage:
showModalBottomSheet(
context: context,
builder: (_) => MyBottomSheet(),
);
Upvotes: 22
Reputation: 4380
You can use Flutter's StatefulBuilder
to wrap your ModalBottomSheet as follows:
showModalBottomSheet(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState /*You can rename this!*/) {
return Container(
height: heightOfModalBottomSheet,
child: RaisedButton(onPressed: () {
setState(() {
heightOfModalBottomSheet += 10;
});
}),
);
});
});
Please note that the new setState
will override your main widget setState
but sure you can just rename it so you would be able to set state of your parent widget and the modal's
//This sets modal state
setModalState(() {
heightOfModalBottomSheet += 10;
});
//This sets parent widget state
setState(() {
heightOfModalBottomSheet += 10;
});
Upvotes: 395
Reputation: 3758
You can maybe use the showBottomSheet
from the ScaffoldState
. read more here about this showBottomSheet.
This will show the bottomSheet and return a controller PersistentBottomSheetController
. with this controller you can call controller.SetState((){})
which will re-render the bottomSheet.
Here is an example
PersistentBottomSheetController _controller; // <------ Instance variable
final _scaffoldKey = GlobalKey<ScaffoldState>(); // <---- Another instance variable
.
.
.
void _incrementBottomSheet(){
_controller.setState(
(){
heightOfModalBottomSheet += 10;
}
)
}
.
void _createBottomSheet() async{
_controller = await _scaffoldKey.currentState.showBottomSheet(
context: context,
builder: (context) {
return Container(
height: heightOfModalBottomSheet,
child: RaisedButton(
onPressed: () {
_incrementBottomSheet()
}),
);
});
}
Upvotes: 37
Reputation: 21758
Please refer to the below working code. I created a new Stateful widget(ModalBottomSheet
) for the showModalBottomSheet
. On button press, we are rebuilding the ModalBottomSheet
only which is much cleaner now. We can use AnimationController
if need animation for changing the height.
import 'dart:async';
import 'package:flutter/material.dart';
class ModalBottomSheet extends StatefulWidget {
_ModalBottomSheetState createState() => _ModalBottomSheetState();
}
class _ModalBottomSheetState extends State<ModalBottomSheet>
with SingleTickerProviderStateMixin {
var heightOfModalBottomSheet = 100.0;
Widget build(BuildContext context) {
return Container(
height: heightOfModalBottomSheet,
child: RaisedButton(
child: Text("Press"),
onPressed: () {
heightOfModalBottomSheet += 100;
setState(() {});
}),
);
}
}
class MyHomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return new _MyHomePageState();
}
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
Future(() => showModalBottomSheet(
context: context,
builder: (context) {
return ModalBottomSheet();
}));
return new Scaffold(
appBar: new AppBar(
title: new Text("Modal example"),
),
);
}
}
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(title: 'Flutter Demo', home: new MyHomePage());
}
}
Upvotes: 12