How to make an Expandable ListView using Flutter like the screenshot below?
I want to make a scrollable list view of ExpansionTileswhich when expanded shows a non-scrollable list view.
I tried to implement list view of ExpansionTiles inside which I nested another list view using listView.builder(...). But when I expanded the ExpansionTile the list view didn't show up...
(The screenshot is for illustrative purpose)
Is there a way to get similar output in Flutter?
EDIT: My Source Code:
import 'package:flutter/material.dart';
void main() => runApp(
new MaterialApp(
home: new MyApp(),
)
);
var data = {
"01/01/2018": [
["CocaCola", "\$ 5"],
["Dominos Pizza", "\$ 50"],
],
"04/01/2018": [
["Appy Fizz", "\$ 10"],
["Galaxy S9+", "\$ 700"],
["Apple iPhone X", "\$ 999"],
],
};
List<String> dataKeys = data.keys.toList();
String getFullDate(String date) {
List<String> dateSplit = date.split('/');
List<String> months = ["Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sep", "Oct", "Nov", "Dec"];
return "${dateSplit[0]} ${months[int.parse(dateSplit[1]) - 1]} ${dateSplit[2]}";
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<Widget> _buildList(int keyIndex) {
List<Widget> list = [];
for (int i = 0; i < data[dataKeys[keyIndex]].length; i++) {
list.add(
new Row(
children: <Widget>[
new CircleAvatar(
child: new Icon(Icons.verified_user),
radius: 20.0,
),
new Text(data[dataKeys[keyIndex]][i][0])
],
)
);
}
return list;
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Expense Monitor"),
),
body: new Container (
child: new ListView.builder(
itemCount: dataKeys.length,
itemBuilder: (BuildContext context, int keyIndex) {
return new Card(
child: new ExpansionTile(
title: new Text(getFullDate(dataKeys[keyIndex])),
children: <Widget>[
new Column(
children: _buildList(keyIndex)
)
]
),
);
}
)
)
);
}
}
Error as shown in Console:
I/flutter (12945): ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter (12945): The following assertion was thrown during performResize():
I/flutter (12945): Vertical viewport was given unbounded height.
I/flutter (12945): Viewports expand in the scrolling direction to fill their container.In this case, a vertical
I/flutter (12945): viewport was given an unlimited amount of vertical space in which to expand. This situation
I/flutter (12945): typically happens when a scrollable widget is nested inside another scrollable widget.
I/flutter (12945): If this widget is always nested in a scrollable widget there is no need to use a viewport because
I/flutter (12945): there will always be enough vertical space for the children. In this case, consider using a Column
I/flutter (12945): instead. Otherwise, consider using the "shrinkWrap" property (or a ShrinkWrappingViewport) to size
I/flutter (12945): the height of the viewport to the sum of the heights of its children.
I/flutter (12945): When the exception was thrown, this was the stack:
I/flutter (12945): #0 RenderViewport.performResize.<anonymous closure> (package:flutter/src/rendering/viewport.dart:944:15)
I/flutter (12945): #1 RenderViewport.performResize (package:flutter/src/rendering/viewport.dart:997:6)
I/flutter (12945): #2 RenderObject.layout (package:flutter/src/rendering/object.dart:1555:9)
I/flutter (12945): #3 _RenderProxyBox&RenderBox&RenderObjectWithChildMixin&RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:109:13)
......
I/flutter (12945): ════════════════════════════════════════════════════════════════════════════════════════════════════
I/flutter (12945): Another exception was thrown: RenderBox was not laid out: RenderViewport#df29c NEEDS-LAYOUT NEEDS-PAINT
Try this:
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: MyApp(), debugShowCheckedModeBanner: false,),);
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: vehicles.length,
itemBuilder: (context, i) {
return ExpansionTile(
title: Text(vehicles[i].title, style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold, fontStyle: FontStyle.italic),),
children: <Widget>[
Column(
children: _buildExpandableContent(vehicles[i]),
),
],
);
},
),
);
}
_buildExpandableContent(Vehicle vehicle) {
List<Widget> columnContent = [];
for (String content in vehicle.contents)
columnContent.add(
ListTile(
title: Text(content, style: TextStyle(fontSize: 18.0),),
leading: Icon(vehicle.icon),
),
);
return columnContent;
}
}
class Vehicle {
final String title;
List<String> contents = [];
final IconData icon;
Vehicle(this.title, this.contents, this.icon);
}
List<Vehicle> vehicles = [
Vehicle(
'Bike',
['Vehicle no. 1', 'Vehicle no. 2', 'Vehicle no. 7', 'Vehicle no. 10'],
Icons.motorcycle,
),
Vehicle(
'Cars',
['Vehicle no. 3', 'Vehicle no. 4', 'Vehicle no. 6'],
Icons.directions_car,
),
];
Try this:
First Make an ExpandableContainer using AnimatedContainer.
Then Make an ExpandableListView which will create a Column . The first child of Column will be a button to expand and Second will be ExpandableContainer .
ExpandableContainer will have a ListView as its child.
The last step will be to make a ListView of ExpandableListView.
The Result :
The Code :
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new Home()));
}
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.grey,
appBar: new AppBar(
title: new Text("Expandable List"),
backgroundColor: Colors.redAccent,
),
body: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new ExpandableListView(title: "Title $index");
},
itemCount: 5,
),
);
}
}
class ExpandableListView extends StatefulWidget {
final String title;
const ExpandableListView({Key key, this.title}) : super(key: key);
#override
_ExpandableListViewState createState() => new _ExpandableListViewState();
}
class _ExpandableListViewState extends State<ExpandableListView> {
bool expandFlag = false;
#override
Widget build(BuildContext context) {
return new Container(
margin: new EdgeInsets.symmetric(vertical: 1.0),
child: new Column(
children: <Widget>[
new Container(
color: Colors.blue,
padding: new EdgeInsets.symmetric(horizontal: 5.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new IconButton(
icon: new Container(
height: 50.0,
width: 50.0,
decoration: new BoxDecoration(
color: Colors.orange,
shape: BoxShape.circle,
),
child: new Center(
child: new Icon(
expandFlag ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down,
color: Colors.white,
size: 30.0,
),
),
),
onPressed: () {
setState(() {
expandFlag = !expandFlag;
});
}),
new Text(
widget.title,
style: new TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
)
],
),
),
new ExpandableContainer(
expanded: expandFlag,
child: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new Container(
decoration:
new BoxDecoration(border: new Border.all(width: 1.0, color: Colors.grey), color: Colors.black),
child: new ListTile(
title: new Text(
"Cool $index",
style: new TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
),
leading: new Icon(
Icons.local_pizza,
color: Colors.white,
),
),
);
},
itemCount: 15,
))
],
),
);
}
}
class ExpandableContainer extends StatelessWidget {
final bool expanded;
final double collapsedHeight;
final double expandedHeight;
final Widget child;
ExpandableContainer({
#required this.child,
this.collapsedHeight = 0.0,
this.expandedHeight = 300.0,
this.expanded = true,
});
#override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return new AnimatedContainer(
duration: new Duration(milliseconds: 500),
curve: Curves.easeInOut,
width: screenWidth,
height: expanded ? expandedHeight : collapsedHeight,
child: new Container(
child: child,
decoration: new BoxDecoration(border: new Border.all(width: 1.0, color: Colors.blue)),
),
);
}
}
Screenshot:
Code:
class MyPage extends StatelessWidget {
List<Widget> _getChildren(int count, String name) => List<Widget>.generate(
count,
(i) => ListTile(title: Text('$name$i')),
);
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: [
ExpansionTile(
title: Text('List-A'),
children: _getChildren(4, 'A-'),
),
ExpansionTile(
title: Text('List-B'),
children: _getChildren(3, 'B-'),
),
],
),
);
}
}
Related
I am working on diary flutter android application with firebase real time database.
I am trying to make three functions: add, delete, edit.
For now, add and delete functions work well but edit part causes problems.
When I edit the texts and click 'save', it is updated to firebase itself but it shows "type 'String' is not a subtype of type 'Map<dynamic, dynamic>' in type cast" this error. Edit function can be done by clicking value from firebase animated list.
I put lots of effort to fix it and found what is problem with String but couldn't solve it.
Could anyone help me to solve this? Thank you a lot.
Below are models.
//1. Main
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
appBarTheme: AppBarTheme(color: Color(0xfff5f5f5),foregroundColor:Color(0xff0d47a1)),
splashColor: Color(0xff00bcd4),
scaffoldBackgroundColor: Color(0xffe9f1f2),
hoverColor: Color(0xff26c6da),
shadowColor: Colors.black,
fontFamily: 'Itim',
),
home: MainPage()));
}
//2. Main Page
class MainPage extends StatefulWidget {
MainPage({Key? key}) : super(key: key);
#override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
late PageController _myPage;
int selectedPage = 0;
void initState() {
super.initState();
_myPage = PageController(initialPage: 0);
selectedPage = 0;
}
List<IconData> iconlist = [
Icons.home_outlined,
Icons.add,
Icons.settings,
Icons.person,
];
List<Widget> _currentpage = [
ListOfNotes(),
AddNotes(),
SettingsPage(),
AccountPage(),
];
int _bottomnavindex = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'NOTES',
style: TextStyle(fontFamily: 'Graduate', color: Color(0xff0d47a1)),
),
),
body: _currentpage[_bottomnavindex],
floatingActionButton: FloatingActionButton(
child: Icon(Icons.sunny),
onPressed: () {},
backgroundColor: Color(0xff039be5),
),
floatingActionButtonLocation:
FloatingActionButtonLocation.miniEndDocked,
bottomNavigationBar: AnimatedBottomNavigationBar(
leftCornerRadius: 30,
//rightCornerRadius: 0,
gapLocation: GapLocation.end,
activeColor: Color(0xffe91e63),
inactiveColor: Color(0xff0d47a1),
icons: iconlist,
activeIndex: _bottomnavindex,
onTap: (index) => setState(() {
_bottomnavindex = index;
selectedPage = index;
}),
),
);
}
}
//3. AddNotes
class AddNotes extends StatefulWidget {
AddNotes({Key? key}) : super(key: key);
#override
State<AddNotes> createState() => _AddNotesState();
}
class _AddNotesState extends State<AddNotes> {
late PageController _myPage;
bool _validate=false;
TextEditingController _controller1 = TextEditingController();
TextEditingController _controller2 = TextEditingController();
final messageDao = MessageDao();
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Center(
child: Container(
height: 500,
width: 300,
decoration: BoxDecoration(
color: Color(0xffe1e9f0),
borderRadius: BorderRadius.all(Radius.circular(30.0)),
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(left: 18.0),
child: TextField(
enableInteractiveSelection: true,
keyboardType: TextInputType.text,
textInputAction: TextInputAction.newline,
minLines:1,
decoration: InputDecoration(
errorText: (_validate)?'CANT BE NULL':null,
hintText: 'NOTE TITLE',
border: InputBorder.none,
hintStyle: TextStyle(
fontFamily: 'Graduate', color: Color(0xffc3dbf0)),),
controller: _controller1,
autocorrect: false,
style: TextStyle(
fontFamily: 'Itim', color: Color(0xff191176)),
),
),
Padding(
padding: const EdgeInsets.only(left: 18.0),
child: TextField(
enableInteractiveSelection: true,
keyboardType: TextInputType.text,
textInputAction: TextInputAction.newline,
minLines:1,
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'NOTE CONTENT',
hintStyle: TextStyle(
fontFamily: 'Graduate', color: Color(0xffc3dbf0)),),
controller: _controller2,
style: TextStyle(
fontFamily: 'Itim', color: Color(0xff191176)),
),
),
],
),
),
),
floatingActionButton: FloatingActionButton.small(onPressed: () {
setState(() {
_controller1.text.isEmpty ? _validate = true : _validate = false;
if(!_validate){
add();
}
});
},
backgroundColor: Color(0xffeab9d6),
hoverColor: Colors.white,
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20))),
child: Text('OK', style: TextStyle(fontFamily: 'Graduate',color: Color(0xffcd087c)),),),
),
);
}
void add(){
final message = NotesOfApp(_controller1.text,_controller2.text, DateTime.now(), DateTime.now(),DateTime.now().microsecondsSinceEpoch);
messageDao.saveNotes(message);
_controller1.clear();
_controller2.clear();
}
}
//4. AddNotesToList
class MessageDao {
final DatabaseReference _notesRef = FirebaseDatabase.instance.ref();
void saveNotes(NotesOfApp notes) {
_notesRef.push().set(notes.toJson());
}
Query getNotesQuery() {
return _notesRef;
}
}
//5. Notes
class NotesOfApp {
final String NoteTitle;
final String NoteContent;
final DateTime created;
final DateTime editted;
final int date;
NotesOfApp(this.NoteTitle, this.NoteContent,this.created,this.editted,this.date);
NotesOfApp.fromJson(Map<dynamic, dynamic> json)
: created = DateTime.parse(json['created']),
editted = DateTime.parse(json['editted']),
NoteTitle = json['NoteTitle'] as String,
NoteContent = json['NoteContent'] as String,
date=json['date'] ;
Map<dynamic, dynamic> toJson() => <dynamic, dynamic>{
'created' : created.toIso8601String().split('T').first,
'editted' : editted.toIso8601String().split('T').first,
'NoteTitle': NoteTitle,
'NoteContent' :NoteContent,
'date':DateTime.now().microsecondsSinceEpoch,
};
}
//6. List of notes
class ListOfNotes extends StatelessWidget {
final reference = FirebaseDatabase.instance.ref().orderByChild('date');
List<String> keys=[];
List<Color> colors = [
Color(0xffefdada),
Color(0xffefe6e9),
Color(0xfff9f0fa),
Color(0xfff1eef5),
Color(0xfff8fae2),
Color(0xfffffcf2),
Color(0xfffff3ef)
];
ListOfNotes({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Container(
height: double.infinity,
child: FirebaseAnimatedList(
itemBuilder: (BuildContext context, DataSnapshot snapshot,
Animation<double> animation, int index) {
Map notes = snapshot.value as Map;
for(DataSnapshot i in snapshot.children ) {
keys.add(i.key.toString());
break;
}
return _buildList(context, notes: (notes));
},
query: reference,
),
)));
}
Widget _buildList(BuildContext context, {required Map notes}) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 8),
child: Container(
height: 80,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(15)),
color: colors[Random().nextInt(colors.length)],
),
child: Column(
//mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListTile(
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(15))),
title: Text(notes['NoteTitle'],
style: TextStyle(
fontFamily: 'Graduate',
fontSize: 10,
color: Color(0xff191176),
fontWeight: FontWeight.bold)),
subtitle: Text(notes['editted'],
style: TextStyle(
color: Color(0xff191176),
fontFamily: 'Graduate',
fontSize: 10,
fontWeight: FontWeight.bold)),
tileColor: colors[Random().nextInt(colors.length)],
onLongPress: () {
showAnimatedDialog(
animationType: DialogTransitionType.slideFromTop,
curve: Curves.easeInOutCubicEmphasized,
duration: Duration(seconds: 1),
context: (context),
builder: (builder) => Warning(
NoteTitle: notes['NoteTitle'],
));
},
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => NoteContents(
NoteTitle: notes['NoteTitle'],
NoteContent: notes['NoteContent'],
list:keys,
)));
},
),
SizedBox(
height: 8,
),
]),
));
}
}
//7. Note Content
class NoteContents extends StatelessWidget {
final String NoteContent;
final String NoteTitle;
final List<String> list;
NoteContents({Key? key, required this.NoteContent, required this.NoteTitle, required this.list}) : super(key: key);
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Center(
child: Container(
height: 500,
width: 300,
decoration: BoxDecoration(
color: Color(0xffe1e9f0),
borderRadius: BorderRadius.all(Radius.circular(30.0)),
),
child:Column(
children: [
ListTile(
onTap:(){Navigator.of(context).push(MaterialPageRoute(builder: (context)=>UpdateNotes(NoteTitle: NoteTitle, NoteContent:NoteContent,list: list,)));},
title: Padding(
padding: const EdgeInsets.symmetric(vertical: 18),
child: Column(
children:[
Text('$NoteTitle',style:TextStyle(color: Color(0xff191176),fontFamily: 'Graduate',fontWeight: FontWeight.w500)),
SizedBox(height: 10),
Text('$NoteContent',style:TextStyle(color: Color(0xff191176),fontSize:12),),
]),
),
onLongPress: (){Navigator.of(context).push(MaterialPageRoute(builder: (context)=>UpdateNotes(NoteTitle: NoteTitle, NoteContent:NoteContent,list: list,)));},
)
],
),)),
floatingActionButton: FloatingActionButton.small(
onPressed: () {
// print(NoteTitle);
// print(NoteContent);
Navigator.of(context).pop();
},
backgroundColor: Color(0xffeab9d6),
hoverColor: Colors.white,
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10))),
child: Text('BACK',
style: TextStyle(
fontFamily: 'Graduate',
color: Color(0xffcd087c),
fontSize: 8)),
),
),
);
}
}
//8. Update
class UpdateNotes extends StatelessWidget {
final String NoteTitle, NoteContent;
final List<String> list;
UpdateNotes({Key? key, required this.NoteTitle, required this.NoteContent,required this.list})
: super(key: key);
final controller1 = TextEditingController(),
controller2 = TextEditingController();
#override
Widget build(BuildContext context) {
controller1.text = '$NoteTitle';
controller2.text = '$NoteContent';
return SafeArea(
child: Scaffold(
appBar: AppBar(
backgroundColor: Color(0xffe9f1f2),
shadowColor: Color(0xffe9f1f2),
),
body: Center(
child: Container(
height: 500,
width: 300,
decoration: BoxDecoration(
color: Color(0xffe1e9f0),
borderRadius: BorderRadius.all(Radius.circular(30.0)),
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(12.0),
child: TextField(
decoration: InputDecoration(
border: InputBorder.none,
),
style: TextStyle(color: Color(0xff191176)),
controller: controller1,
),
),
SizedBox(
height: 12,
),
Padding(
padding: const EdgeInsets.all(12.0),
child: TextField(
minLines: 1,
maxLines: 500,
decoration: InputDecoration(
border: InputBorder.none,
),
style: TextStyle(color: Color(0xff191176)),
controller: controller2,
),
),
],
),
),
),
floatingActionButton: FloatingActionButton.small(
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(15))),
backgroundColor: Color(0xffeab9d6),
hoverColor: Colors.white,
onPressed: () {
showAnimatedDialog(
animationType: DialogTransitionType.fade,
curve: Curves.easeInOutCubicEmphasized,
duration: Duration(seconds: 1),
context: (context),
builder: (builder) => Warning2(
NoteTitle: controller1.text,
NoteContent: controller2.text,
list: list,
));
},
child: Text('SAVE',
style: TextStyle(
fontFamily: 'Graduate',
color: Color(0xffcd087c),
fontSize: 8)),),
),
);
}
}
//9. Update pop up
class Warning2 extends StatelessWidget {
final List<String> list;
Warning2({Key? key, required this.NoteTitle, required this.NoteContent, required this.list}) : super(key: key);
final String NoteTitle,NoteContent;
#override
Widget build(BuildContext context) {
return ClassicGeneralDialogWidget(
titleText: 'SURE WANT TO UPDATE ?',
onPositiveClick:(){
Update('$NoteTitle','$NoteContent',list);
Navigator.of(context).pop();
} ,
onNegativeClick: (){
Navigator.of(context).pop();
}
);
}
}
Future<void> Update(String NoteTitle,String NoteContent,List<String> list) async {
String key="";
String result="";
DatabaseReference ref = FirebaseDatabase.instance.ref();
final snapshot = await FirebaseDatabase.instance.ref().get();
for(DataSnapshot i in snapshot.children ){
key = i.key.toString();
for(int i=0;i<list.length;i++){
if(key==list[i]){
result=list[i];
}
}
}
await ref.child(result).update({
"NoteTitle":"$NoteTitle",
"NoteContent":"$NoteContent",
"editted":DateTime.now().toIso8601String().split('T').first,
});
}
//10. delete pop up
void check(String NoteTitle) async{
print(NoteTitle);
final reference = FirebaseDatabase.instance.ref();
String key="";
final snapshot = await FirebaseDatabase.instance.ref().get();
for(DataSnapshot i in snapshot.children ){
if(i.child('NoteTitle').value.toString()==NoteTitle) {
key = i.key.toString();
break;
}
}
reference.child(key).remove();
}
class Warning extends StatelessWidget {
Warning({Key? key, this.NoteTitle}) : super(key: key);
final String? NoteTitle;
#override
Widget build(BuildContext context) {
return ClassicGeneralDialogWidget(
titleText: 'SURE WANT TO DELETE ?',
onPositiveClick:(){
check('$NoteTitle');
Navigator.of(context).pop();
} ,
onNegativeClick: (){
Navigator.of(context).pop();
}
);
}
}
These are codes and in' Notes' model, there is "Map<dynamic, dynamic>".
These dependencies are from pubspec.yaml.
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
animated_bottom_navigation_bar: ^1.0.1
firebase_database: ^9.0.20
firebase_core: ^1.20.0
flutter_animated_dialog: ^2.0.1
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter:
uses-material-design: true
fonts:
- family: Graduate
fonts:
- asset: fonts/Graduate-Regular.ttf
- family: Itim
fonts:
- asset: fonts/Itim-Regular.ttf
And this is how error shows on application. Other things on page are showed well and other pages work well too. But only this content part is missing.
And this is the full error code.
======== Exception caught by widgets library =======================================================
The following _CastError was thrown building:
type 'String' is not a subtype of type 'Map<dynamic, dynamic>' in type cast
When the exception was thrown, this was the stack:
#0 ListOfNotes.build.<anonymous closure> (package:diaryreal/ListOfNotes.dart:32:46)
#1 FirebaseAnimatedListState._buildItem (package:firebase_database/ui/firebase_animated_list.dart:209:30)
#2 SliverAnimatedListState._itemBuilder (package:flutter/src/widgets/animated_list.dart:624:30)
#3 SliverChildBuilderDelegate.build (package:flutter/src/widgets/sliver.dart:471:22)
#4 SliverMultiBoxAdaptorElement._build (package:flutter/src/widgets/sliver.dart:1236:28)
#5 SliverMultiBoxAdaptorElement.performRebuild.processElement (package:flutter/src/widgets/sliver.dart:1169:67)
#6 Iterable.forEach (dart:core/iterable.dart:325:35)
#7 SliverMultiBoxAdaptorElement.performRebuild (package:flutter/src/widgets/sliver.dart:1213:24)
#8 SliverMultiBoxAdaptorElement.update (package:flutter/src/widgets/sliver.dart:1146:7)
#9 Element.updateChild (package:flutter/src/widgets/framework.dart:3530:15)
#10 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4832:16)
#11 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4977:11)
#12 Element.rebuild (package:flutter/src/widgets/framework.dart:4529:5)
#13 StatefulElement.update (package:flutter/src/widgets/framework.dart:5009:5)
#14 Element.updateChild (package:flutter/src/widgets/framework.dart:3530:15)
#15 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6222:14)
#16 Element.updateChild (package:flutter/src/widgets/framework.dart:3530:15)
#17 RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:5825:32)
#18 MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6375:17)
#19 _ViewportElement.update (package:flutter/src/widgets/viewport.dart:237:11)
#20 Element.updateChild (package:flutter/src/widgets/framework.dart:3530:15)
And this is what I got 'debug' tab.
I write the code to add and remove columns inside Gridview at first load screen everything looks fine but when I remove any column on click the cross button inside the column it breaks the layout and does not remove any column whenever i click to remove column it shows error
Error: SliverGeometry is not valid: The "maxPaintExtent" is less than the "paintExtent".
Error Image: Error of layout during click/remove item
Here is my code :-
import 'package:flutter/material.dart';
import 'package:mindmatch/utils/widget_functions.dart';
import 'package:mindmatch/custom/BorderIcon.dart';
import 'package:mindmatch/screens/Relation.dart';
import 'package:flutter_svg/flutter_svg.dart';
void main() {
runApp(new MaterialApp(
home: new Photos(),
));
}
class Photos extends StatefulWidget {
var usrid;
Photos({Key? key, #required this.usrid}) : super(key: key);
#override
_Photos createState() => _Photos();
}
class _Photos extends State<Photos>{
int counter = 0;
//List<Widget> _list = new List<Widget>();
List<Widget> _list = <Widget> [];
#override
void initState() {
for(int i =0; i < 4; i++){
Widget child = _newItem(i);
_list.add(child);
}
}
void on_Clicked() {
Widget child = _newItem(counter);
setState(() => _list.add(child));
}
Widget _newItem(int i) {
Key key = new Key('item_${i}');
Column child = Column(
key: key,
children: [
Stack(
children: [
Card(
elevation: 0,
shape: RoundedRectangleBorder(
side: BorderSide(
color: Color(0xffa1a1a1),
),
borderRadius: BorderRadius.all(Radius.circular(12)),
),
child: SizedBox(
//width: 300,
height: 100,
child: Center(child:
Icon(
Icons.image,
color: Color(0xffcccccc),
size: 60,
),
),
),
),
Positioned(
top: 9,
right: 9,
child: InkWell(
onTap: () => _removeItem(key),
child: SvgPicture.asset(
width: 20,
'assets/images/close.svg',
height: 20,
),
),
)
]
),
]
);
counter++;
return child;
}
void _removeItem(Key key){
for(int i = 0; i < _list.length; i++){
Widget child = _list.elementAt(i);
if(child.key == key){
setState(() => _list.removeAt(i));
print('Removing ${key.toString()}');
}
}
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Photos'),
backgroundColor: Colors.deepPurpleAccent,
),
floatingActionButton: new FloatingActionButton(onPressed: on_Clicked, child: new
Icon(Icons.add),),
body: new Container(
padding: new EdgeInsets.all(32.0),
child: Column(
children: [
Expanded(
child: GridView(
//padding: const EdgeInsets.all(8.0),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 10.0,
mainAxisSpacing: 15,
//childAspectRatio: 2/1,
),
children: _list,
)
)
],
),
),
);
}
}
And here is my output:- Output image of code where clearly see add and remove functionality
Please help out how I did this task and how I add and remove columns inside Gridview
anyone, please help me.
And this is what I need to do:- this is actual layout i want to create
So please help me
You should not operate the widget directly but data instead. What you should store in the list is the data (image URL or some string, which can be a very complex data structure) you want to present in the view and make the view respond to the changes happening on the object list with flutter's setState function.
try this:
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
home: new Photos(),
));
}
class Photos extends StatefulWidget {
var usrid;
Photos({Key? key, #required this.usrid}) : super(key: key);
#override
_Photos createState() => _Photos();
}
class _Photos extends State<Photos> {
int counter = 0;
//List<Widget> _list = new List<Widget>();
List<String> _list = <String>[];
#override
void initState() {
for (int i = 0; i < 4; i++) {
_list.add("your data -obj");
}
}
void on_Clicked() {
setState(() => _list.add("your data -obj"));
}
Widget _newItem(int i) {
Key key = new Key('item_${i}');
Column child = Column(key: key, children: [
Stack(children: [
Card(
elevation: 0,
shape: RoundedRectangleBorder(
side: BorderSide(
color: Color(0xffa1a1a1),
),
borderRadius: BorderRadius.all(Radius.circular(12)),
),
child: SizedBox(
//width: 300,
height: 80,
child: Center(
child: Icon(
Icons.image,
color: Color(0xffcccccc),
size: 60,
),
),
),
),
Positioned(
top: 9,
right: 9,
child: InkWell(onTap: () => _removeItem(i), child: Text('close')),
)
]),
]);
counter++;
return child;
}
void _removeItem(int i) {
print("====remove $i");
print('===Removing $i');
setState(() => _list.removeAt(i));
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Photos'),
backgroundColor: Colors.deepPurpleAccent,
),
floatingActionButton: new FloatingActionButton(
onPressed: on_Clicked,
child: new Icon(Icons.add),
),
body: new Container(
padding: new EdgeInsets.all(32.0),
child: Column(
children: [
Expanded(
child: GridView(
//padding: const EdgeInsets.all(8.0),
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 10.0,
mainAxisSpacing: 15,
//childAspectRatio: 2/1,
),
children: List.generate(_list.length, (index) {
//generating tiles with people from list
return _newItem(index);
})))
],
),
),
);
}
}
i have setup banner ads in flutter and those are overlapping the bottom navigation bar
I want to display ads below that bottom navigation bar,
is there any way that i can add a margin below the bottom navigation bar ?
i have implemented ads in home.dart (mainpage)
import 'package:provider/provider.dart';
import '../../ui/widgets/bottom_nav_bar.dart';
import '../../core/utils/theme.dart';
import 'search_page.dart';
import 'category.dart';
import 'main_page.dart';
import 'settings.dart';
import 'package:flutter/material.dart';
import 'package:firebase_admob/firebase_admob.dart';
import 'for_you.dart';
const String AD_MOB_APP_ID = 'ca-app-pub-3940256099942544~3347511713';
const String AD_MOB_TEST_DEVICE = 'DEC3010B2445165B43EB949F5D97D0F8 - run ad then check device logs for value';
const String AD_MOB_AD_ID = 'ca-app-pub-3940256099942544/6300978111';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
BannerAd _bannerAd;
static final MobileAdTargetingInfo targetingInfo = new MobileAdTargetingInfo(
testDevices: <String>[AD_MOB_TEST_DEVICE],
);
BannerAd createBannerAd() {
return new BannerAd(
adUnitId: AD_MOB_AD_ID,
size: AdSize.banner,
targetingInfo: targetingInfo,
);
}
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
int _selectedIndex = 0;
final PageController _pageController = PageController();
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
_pageController.dispose();
}
#override
Widget build(BuildContext context) {
final stateData = Provider.of<ThemeNotifier>(context);
final ThemeData state = stateData.getTheme();
FirebaseAdMob.instance.initialize(appId: AD_MOB_APP_ID);
_bannerAd = createBannerAd()..load()..show();
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
centerTitle: false,
backgroundColor: state.primaryColor,
elevation: 0,
title: Text(
'RevWalls',
style: state.textTheme.headline,
),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.search,
color: state.textTheme.body1.color,
),
onPressed: () => showSearch(
context: context, delegate: WallpaperSearch(themeData: state)),
)
],
),
body: Container(
color: state.primaryColor,
child: PageView(
controller: _pageController,
physics: BouncingScrollPhysics(),
onPageChanged: (index) {
setState(() {
_selectedIndex = index;
});
},
children: <Widget>[
MainBody(),
Category(),
ForYou(),
SettingsPage(),
],
),
),
bottomNavigationBar: BottomNavyBar(
selectedIndex: _selectedIndex,
unselectedColor: state.textTheme.body1.color,
onItemSelected: (index) {
_pageController.jumpToPage(index);
},
selectedColor: state.accentColor,
backgroundColor: state.primaryColor,
showElevation: false,
items: [
BottomNavyBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavyBarItem(
icon: Icon(Icons.category),
title: Text('Subreddits'),
),
BottomNavyBarItem(
icon: Icon(Icons.phone_android),
title: Text('Exact Fit'),
),
BottomNavyBarItem(
icon: Icon(Icons.settings),
title: Text('Settings'),
),
],
),
);
}
Widget oldBody(ThemeData state) {
return NestedScrollView(
headerSliverBuilder: (BuildContext context, bool boxIsScrolled) {
return <Widget>[
SliverAppBar(
backgroundColor: state.primaryColor,
elevation: 4,
title: Text(
'reWalls',
style: state.textTheme.headline,
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search, color: state.accentColor),
onPressed: () {
showSearch(
context: context,
delegate: WallpaperSearch(themeData: state));
},
)
],
floating: true,
pinned: _selectedIndex == 0 ? false : true,
snap: false,
centerTitle: false,
),
];
},
body: Container(
color: state.primaryColor,
child: PageView(
controller: _pageController,
onPageChanged: (index) {
setState(() {
_selectedIndex = index;
});
},
children: <Widget>[
MainBody(),
Category(),
ForYou(),
SettingsPage(),
],
),
),
);
}
}
and here is the bottom navigation bar -
library bottom_navy_bar;
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class BottomNavyBar extends StatelessWidget {
final int selectedIndex;
final double iconSize;
final Color backgroundColor, selectedColor, unselectedColor;
final bool showElevation;
final Duration animationDuration;
final List<BottomNavyBarItem> items;
final ValueChanged<int> onItemSelected;
BottomNavyBar(
{Key key,
this.selectedIndex = 0,
this.showElevation = true,
this.iconSize = 20,
this.backgroundColor,
this.selectedColor,
this.unselectedColor,
this.animationDuration = const Duration(milliseconds: 250),
#required this.items,
#required this.onItemSelected}) {
assert(items != null);
assert(items.length >= 2 && items.length <= 5);
assert(onItemSelected != null);
}
Widget _buildItem(BottomNavyBarItem item, bool isSelected) {
return AnimatedContainer(
width: isSelected ? 120 : 50,
height: double.maxFinite,
duration: animationDuration,
decoration: BoxDecoration(
color: isSelected ? selectedColor.withOpacity(0.2) : backgroundColor,
borderRadius: BorderRadius.all(Radius.circular(100.0)),
),
alignment: Alignment.center,
child: ListView(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 8),
child: IconTheme(
data: IconThemeData(
size: iconSize,
color: isSelected ? selectedColor : unselectedColor),
child: item.icon,
),
),
isSelected
? DefaultTextStyle.merge(
style: TextStyle(
fontSize: 16,
color: selectedColor,
fontWeight: FontWeight.bold),
child: item.title,
)
: SizedBox.shrink()
],
)
],
),
);
}
#override
Widget build(BuildContext context) {
return Container(
color: backgroundColor,
child: Container(
width: double.infinity,
height: 55,
padding: EdgeInsets.all(8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: items.map((item) {
var index = items.indexOf(item);
return GestureDetector(
onTap: () {
onItemSelected(index);
},
child: _buildItem(item, selectedIndex == index),
);
}).toList(),
),
),
);
}
}
class BottomNavyBarItem {
final Icon icon;
final Text title;
BottomNavyBarItem({
#required this.icon,
#required this.title,
}) {
assert(icon != null);
assert(title != null);
}
}
Please help
adding builder like this will solve the problem
var paddingBottom = 60.0;
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
//
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'World General info',
//theme: ThemeData(primarySwatch: Colors.cyan,),
theme: ThemeData(
primaryColor: Colors.cyan,
accentColor: Colors.green,
textTheme: TextTheme(bodyText2: TextStyle(color: Colors.purple)),
),
home: MyHomePage(title: 'World General Info'),
builder: (BuildContext context, Widget child) {
return new Padding(
child: child,
padding: EdgeInsets.only(bottom: paddingBottom),
);
}
);
}
}
You can use the MediaQuery.of(context) for that.
Wrap the whole Code inside a Container of height: MediaQuery.of(context).size.height - 60 . (the height of ad)
Column(
children: [
Container(
height: MediaQuery.of(context).size.height - 60,
child: HomePage(),
),
BannerAd(),
]
);
Found answer myself
We can use this to set margin in a container with other things like height, width
This code will add margin to bottom of bottom nav bar, as per my need i want to show ads below navbar so this solves my problem
margin: const EdgeInsets.only(bottom: 50)
Add this to your scaffold
bottomNavigationBar: Container(
height: 50.0,
color: Colors.white,
),
Think Simplest bro ... wrap your column ( mainAxisSize : MainAxisSize.min )
Scaffold(
appBar: _appBar,
body: _body,
bottomNavigationBar: Column(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [
Container(
color: Colors.amber,
height: 70,
child: Center(child: Text('Banner'))),
BottomNavigationBar(
items: const [
BottomNavigationBarItem(icon: Icon(Icons.add), label: 'Add'),
BottomNavigationBarItem(icon: Icon(Icons.add), label: 'Add'),
BottomNavigationBarItem(icon: Icon(Icons.add), label: 'Add')
],
)
]))
android studio 3.6
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: new ThemeData(
primaryColor: new Color(Constants.COLOR_PRIMARY),
primaryTextTheme: TextTheme(headline6: TextStyle(color: Colors.white))),
home: new SignInForm());
}
}
class SignInForm extends StatefulWidget {
#override
State<StatefulWidget> createState() {
logger.d("createState:");
return new _SignInFormState();
}
}
class _SignInFormState extends State {
final _formKey = GlobalKey<FormState>();
final _scaffoldKey = GlobalKey<ScaffoldState>();
String _textVersion = "";
String _email = null;
String _password = null;
#override
Widget build(BuildContext context) {
logger.d("build:");
//String _errorMessage = null;
return Scaffold(
appBar: new AppBar(
centerTitle: true,
title: new Text('Sign in',
style: TextStyle(fontWeight: FontWeight.bold))),
body: new Container(
margin: const EdgeInsets.only(
left: Constants.DEFAULT_MARGIN,
right: Constants.DEFAULT_MARGIN),
child: new Form(
key: _formKey,
child: new Column(children: [
new TextFormField(
decoration: new InputDecoration(hintText: 'Email'),
keyboardType: TextInputType.emailAddress,
onChanged: (value) {
setState(() {
_email = value;
});
}),
new TextFormField(
decoration: new InputDecoration(hintText: 'Password'),
obscureText: true),
new Container(
margin: const EdgeInsets.only(
top: Constants.DEFAULT_MARGIN / 2),
height: Constants.MIN_HEIGHT,
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
new Text("Forgot password?",
style: TextStyle(
color: new Color(Constants.COLOR_PRIMARY))),
new Align(
alignment: Alignment.centerRight,
child: new RaisedButton(
// Buttons are disabled by default
child: Text('Sign in'.toUpperCase()),
color: new Color(Constants.COLOR_PRIMARY),
textColor: new Color(
Constants.COLOR_PRIMARY_TEXT_COLOR),
onPressed: () {
if (_formKey.currentState.validate()) {
logger.d(
"onPressed: check_email = $_email");
if (_email == null ||
_email.trim().isEmpty) {
logger.d(
"onPressed: show_error_message");
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text("Аll fields must be filled"),
backgroundColor: Colors.red));
}
}
}))
])),
new Container(
margin: const EdgeInsets.all(Constants.DEFAULT_MARGIN),
child: new Text('Registration'.toUpperCase(),
style: new TextStyle(
color: new Color(Constants.COLOR_PRIMARY),
fontWeight: FontWeight.bold))),
new Container(
margin: const EdgeInsets.all(Constants.DEFAULT_MARGIN),
child: new Text(_textVersion))
]))));
}
press button and get error in
in this line:
Scaffold.of(context).showSnackBar(
logcat:
The context used was: SignInForm
state: _SignInFormState#fe66a
When the exception was thrown, this was the stack:
#0 Scaffold.of (package:flutter/src/material/scaffold.dart:1456:5)
#1 _SignInFormState.build.<anonymous closure> (package:flutter_sample/signinform.dart:86:52)
#2 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:779:14)
#3 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:862:36)
#4 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24)
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#74eb7
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(268.7, 208.0)
finalLocalPosition: Offset(52.7, 18.0)
button: 1
sent tap down
The issue happens because the current passed context doesn't have any matching ancestor (in this case a matching ancestor scaffold).
Option 1:
The easiest way to fix this issue is, in your MyApp, replace:
home: new SignInForm()
with:
home: Scaffold(body: SignInForm())
Option 2:
You have to specify the key property of your scaffold:
Scaffold(key: _scaffoldKey, body: // Your remaining code);
And display the snackbar using:
_scaffoldKey.currentState.showSnackBar(
SnackBar(content: Text("Аll fields must be filled"),
backgroundColor: Colors.red)
);
Option 3:
You can use a Builder widget to fix the issue:
Scaffold(body: Builder(builder: (context) {
return Container(child:// Your widget);
// No other change needed
},);
I personally prefer option 3.
When the Scaffold is actually created in the same build function, the
context argument to the build function can't be used to find the
Scaffold (since it's "above" the widget being returned in the widget
tree).
Reference of method
Currently I am trying to develop a BottomSheet that expands to a specific size. When that size is reached, the user should be able to drag the BottomSheet a little bit up. I have implmented the GestureDetector inside the BottomSheet, so that I am able to detect a vertical drag. The drag function is called, but unfortunately it is not changing the size of the BottomSheet.
This is my code:
//These values are outside of the classes
double initial;
double _kShoppingMenuHeight;
//My custom BottomSheet with rounded corner
Future<T> showRoundedBottomSheet<T> ({
#required BuildContext context,
#required Widget child,
double height
}) {
assert(context != null);
assert(child != null);
return showModalBottomSheet(
context: context,
builder: (BuildContext context){
return new Container(
height: (height != null
? height
: 400.0
),
color: Color(0xFF737373),
child: new Container(
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(5.0),
topRight: const Radius.circular(5.0)
)
),
child: Builder(
builder: (BuildContext context){
return child;
},
)
),
);
}
);
}
//The function that opens the BottomSheet
// this is in another class
return showRoundedBottomSheet(
context: context,
height: _kShoppingMenuHeight,
//Make bottomsheet draggable and fixed at specific point
child: ShoppingMenu(
title: _title("Ihre Listen"),
items: items
)
);
//The stateful widget with the content
return GestureDetector(
onVerticalDragStart: (DragStartDetails details){
initial = details.globalPosition.dy;
},
onVerticalDragUpdate: (DragUpdateDetails details){
setState(() {
_kShoppingMenuHeight = MediaQuery.of(context).size.height / 2 - details.globalPosition.dy;
if(_kShoppingMenuHeight.isNegative) _kShoppingMenuHeight = _kShoppingMenuHeight * (-1);
});
},
onVerticalDragEnd: (DragEndDetails details){
},
child: NotificationListener<OverscrollIndicatorNotification>(
onNotification: (overscroll){
overscroll.disallowGlow();
},
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: _kShoppingMenuHeight
),
child: ListView(
physics: NeverScrollableScrollPhysics(),
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 30.0, left: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 10.0),
child: widget.title,
),
Column(
children: widget.items
)
],
),
),
Divider(),
GestureDetector(
child: ListTile(
leading: Icon(Icons.add, color: Colors.black54),
title: Text(
"Neue Liste erstellen"
),
),
onTap: (){
Navigator.pop(context, "neue Liste");
},
),
Divider(),
GestureDetector(
child: ListTile(
leading: Icon(OMIcons.smsFailed, color: Colors.black54),
title: Text(
"Feedback geben"
),
),
onTap: (){
Navigator.pop(context, "feedback");
},
)
],
),
),
),
);
This is a complete example of how you can drag around with your modal bottom sheet.
The idea is to wrap the content of the sheet by a stream builder ,and update the stream when drag occurs. let me know if you need further explanation.
import 'package:flutter/material.dart';
import 'dart:async';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('My App'),
),
body: MyWidget(),
),
);
}
}
StreamController<double> controller = StreamController.broadcast();
class MyWidget extends StatefulWidget{
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
double position;
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: 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'),
)),
);
});
}),
),
);
}
}
I think setState() call on the wrong widget.
setState() need to be called on the widget holding the Scaffold because bottom sheet belongs to the scaffold itself .
inherited widget may be the solution