I am trying to build a sample with reorderable listview that has expansion tile as its child.
Upon expanding the tile, it will present a listview to the user as follow
Expanded tile with listview nested inside
When all expansion tile are collapsed, i have no issue reordering the tiles by long pressing and moving it. But if one of the tiles are expanded, and user try to reorder the tiles, flutter will throw the following error and the expanded tile will not be able to collapse until hot reload
ScrollController attached to multiple scroll views.
'package:flutter/src/widgets/scroll_controller.dart':
Failed assertion: line 111 pos 12: '_positions.length == 1'
Not Collapsible listview
How should I go about fixing it? The issue seems to stem from having a scroll controller nested in another scroll controller. Is there a way of forcing all expansion tile to collapse upon long pressing it?
Thanks in advance
List<int> a = [1, 2, 3];
class _BlankPageState extends State<BlankPage> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Padding(
padding: EdgeInsets.all(10),
child: ReorderableListView(
onReorder: (oldIndex, newIndex) {
print('now');
setState(
() {
if (newIndex > oldIndex) {
newIndex -= 1;
}
final int item = a.removeAt(oldIndex);
a.insert(newIndex, item);
},
);
},
children: a.map((index) {
return ExpansionTile(
backgroundColor: Colors.grey,
key: Key('$index'),
title: Text('Tile' + '${index.toString()}'),
children: <Widget>[
Container(
height: 100,
child: ListView(children: <Widget>[
Text('This is a test' + '$index'),
Text('This is a test' + '$index'),
]),
)
],
);
}).toList()),
),
),
);
I was able to solve the above issue with the new release of Flutter 1.17 which introduced the following
Change log for Flutter 1.17.0
49148 Exposed optional scrollController property in ReorderableListView
By adding a scroll controller in my reorderablelistview, I no longer encounter the multiple scroll views error above when a list view is nested inside a reorderablelistview widget
ReorderableListView(
scrollController: ScrollController(initialScrollOffset: 50),
Related
Im very new to flutter, Im trying to do scrolling vertically and show horizontal cuisines and vertical restaurants list, for horizontal list it works fine but I don't know how to do this for the vertical list, please advice me with which is the best way to do it if I will have a huge number of restaurants in this list
SliverToBoxAdapter(//horizontal list
child: RestaurantsSlide(title: 'Featured Restaurants',),
),
SliverList(
delegate: SliverChildListDelegate(
[
//How to call the list here
],
),
),
this is the function to return the list of restaurants
void restaurantCardList() {
List<dynamic> restaurantsList = RESTAURANT_DATA;
List<Widget> listItems = [];
restaurantsList.forEach((restaurant) {
listItems.add(
Padding(
padding: const EdgeInsets.only(left: 10.0, right: 10, bottom: 25),
child: RestaurantCard(restaurant: restaurant),
)
);
}
);
}
I have a list of categories in sqlite, I got those rows across streambuilder, then, I create a list of switch widgets. In each switch widget ontoggle event I change the source value and I call setState method, but, this causes the execution of the build event and reset all switch widgets values. After that I changed the code to store all widgets in a variable and in the streambuild if the widgets exists, return the widget list, this works almost well, this update the widgets values in sources, but, the widgets looks like if they were false.
Anyone has a clue?
Regards
StreamBuilder<List<Category>>( <--- this code is in build event
stream: _catBloc.categoriesStream,
builder: (BuildContext context, AsyncSnapshot<List<Category>> snapshot) {
if(!snapshot.hasData) {
return Container(
height: _size.height,
width: _size.width,
child: Center(
child: CircularProgressIndicator(),
),
);
} else if(_categoriesLinked.isEmpty){ <-- this is a map with the list of id and name
column = _createCategories(snapshot.data, widthSwitch);
return column;
} else {
return column;
}
},
),
Container( <-- this is inside _createCategories method
width: width,
child: Row(
children: [
FlutterSwitch(
width: _size.width * 0.06,
height: _size.height * 0.02,
toggleSize: 20.0,
value: _categoriesLinked[e.id],
showOnOff: false,
padding: 2.0,
activeColor: Color.fromRGBO(88, 203, 143, 0.25),
inactiveColor: Color.fromRGBO(224, 233, 240, 0.50),
activeToggleColor: Color.fromRGBO(88, 203, 143, 1.0),
inactiveToggleColor: Color.fromRGBO(78, 88, 96, 0.50),
onToggle: (val) => setState(() {
_categoriesLinked[e.id] = val;
}),
),
SizedBox(width: _size.width * 0.01,),
Flexible(child: Text("${ e.id}. ${ e.name}"))
]
),
)
In this case, you should refactor your code in order to create the switch as another widget, making them rebuilding only themselves.
For the state of the switch, you can simply get it from the parent using GlobalKey<MySwitchState> to access the children state during an onPressed method for example with: mySwitchKey.currentState
You should definitely avoid building stateful widget inside methods. If you feel the need of a method, create a new widget.
Solved see the answers
I am using flip card package to make flip cards.
I have many cards in the same page and I want to flip them all when I press a button.
I used the example in the documentation :
GlobalKey<FlipCardState> cardKey = GlobalKey<FlipCardState>();
#override
Widget build(BuildContext context) {
return FlipCard(
key: cardKey,
flipOnTouch: false,
front: Container(
child: RaisedButton(
onPressed: () => cardKey.currentState.toggleCard(),
child: Text('Toggle'),
),
),
back: Container(
child: Text('Back'),
),
);
}
but I get error Duplicate GlobalKey detected in widget tree. or Multiple widgets used the same GlobalKey
So what I can do to solve this problem ?
I solved this problem with making a map of global keys
var cardKeys = Map<int, GlobalKey<FlipCardState>>();
and in the ListView.builder in itemBuilder I added
cardKeys.putIfAbsent(index, () => GlobalKey<FlipCardState>());
GlobalKey<FlipCardState> thisCard = cardKeys[index];
and in the FlipCard I added key: thisCard
Then I make a simple for loop in the button onPressed function
RaisedButton(
onPressed: () {
for (int i = 0; i < names.length; i++) {
cardKeys[i].currentState.toggleCard();
}
},
child: Text('Toggle'),
),
Thanks to this answer here
I want to add a button at the end of my GridView. I viewed another similar problem but the code given in the answer does not scroll. Here is the link to that answer.
My design has a similar. Here is a rough sketch.
Also just for clarification, I want the button to appear once the user has scrolled to the end of the grid view.
I am still new to flutter so your help would be much appreciated :)
The thing which you need is ScrollController class.
WHY SCROLLCONTROLLER?
It keeps track of what are we doing with scrolling, that is, scrolling, reached bottom, or top
HOW CAN WE USE IT?
You need to use it inside GridView, to get your things up and running
// Simply initialise your controller in your project
ScrollController _controller = new ScrollController();
// add listener to the controller to find out the scrolling event
_controller.addListener((){});
// pass this into your GridView.
// We we will add it to GridView. ScrollController is for every scrolling widget
// in Flutter
GridView.builder(
controller: _controller,
)
DISCLAIMER: Please do not look at the UI aspect, since we care about the scrolling event tracking and show/hide our button
I have referred to your given link only for creating the UI => Your Provided Link
Also, I have added scrolling event to identify whether we're scrolling or not, but it is commented
The project currently show the button when we reach the bottom, and hide it when we are the top
CODE
class _MyHomePageState extends State<MyHomePage> {
List<String> homeList = [];
//to check whether we have reached bottom
bool isBottom = false;
ScrollController _controller = new ScrollController();
#override
void initState() {
super.initState();
homeList = List.generate(10, (ind) => 'Item $ind').toList();
// adding controller to check whether the page is
// at the bottom
_controller.addListener((){
// reached bottom
if (_controller.offset >= _controller.position.maxScrollExtent &&
!_controller.position.outOfRange) {
setState(() => isBottom = true);
}
// IS SCROLLING
// if (_controller.offset >= _controller.position.minScrollExtent &&
// _controller.offset < _controller.position.maxScrollExtent && !_controller.position.outOfRange) {
// setState(() {
// isBottom = false;
// });
// }
// REACHED TOP
if (_controller.offset <= _controller.position.minScrollExtent &&
!_controller.position.outOfRange) {
setState(() => isBottom = false);
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
height: MediaQuery.of(context).size.height,
child: Stack(
children: [
GridView.builder(
shrinkWrap: true,
controller: _controller,
itemCount: homeList.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, crossAxisSpacing: 20),
itemBuilder: (ctx, i) {
return GestureDetector(
onTap: () => print(i),
child: Container(
margin: EdgeInsets.only(bottom: 20.0),
decoration: BoxDecoration(
color: Colors.indigo[300],
borderRadius: BorderRadius.circular(15.0)
)
)
);
}
),
// if we are bottom we show the button
// else empty container, which is nothing but
// hiding technique in Flutter
isBottom ? Positioned(
bottom: 20,
left: 18,
right: 18,
child: Container(
alignment: Alignment.center,
height: 50,
decoration: BoxDecoration(
color: Colors.orangeAccent,
borderRadius: BorderRadius.circular(15),
),
child: Text('Your widget at the end')
)
) : Container()
]
)
)
);
}
}
RESULT
Getting the error as in the title. Of course this is a straightforward thing to check, but some weird behaviour happens. I'm creating a TabBarView with either 1 or 2 tabs, depending on argument in constructor (numTabs). Now Flutter asserts even when the length of tabs is clearly 2 - the controller length is determined by this value, but widget.children.length in Tabs.dart thinks it has a length of one.
Then more strange behaviour: when the length of the TabController is hardcoded as 1, then the two switch, I then get the error Controller's length property (1) does not match the number of tabs (2) present in TabBar's tabs property. So now it knows there are two tabs in TabBar.
Edit: It should be noted that it only happens when I pass in numTabs as 2. numTabs = 1 works fine and as expected.
Any help will be appreciated. Here is the code (everything inside a stateful widget):
List<Widget> tabs;
List<Widget> tabViews ;
#override
void initState() {
super.initState();
tabs = (widget.numTabs == 1) ? List(1) : List(2) ;
tabViews = (widget.numTabs == 1) ? List(1) : List(2) ;
_tabController = new TabController(vsync: this, length: tabs.length);
}
#override
Widget build(BuildContext context) {
if (widget.numTabs == 1){
tabs[0] = (new Tab(text: "Update"));
tabViews[0] = (_someWidget1);
}else{
tabs = [new Tab(text: "Post 1"), new Tab(text: "Post 2")];
tabViews[0] = (_someWidget1);
tabViews[1] = (_someWidget2);
}
return TabBarView(
controller: _tabController,
children: <Widget>[ Scaffold(
appBar: PreferredSize(
preferredSize:
Size.fromHeight(screenHeight(context, dividedBy: 5)),
child: AppBar(
flexibleSpace: FlexibleSpaceBar(
title: Center(
Text('Title'),
),
),
bottom: TabBar(
controller: _tabController,
tabs: tabs,
),
),
),
body: new TabBarView(
controller: _tabController,
children: tabViews,
)),
],
);
}
This one is a simple problem you can fix it bye if condition
if you can't fix this then you can upload your full file and I will try to fix
if(items.length >= 3){
return getBody(items);
}
else{
return Container();
}