SliverChildBuilderDelegate Not rebuilding its children when the data is changed using setState
Solution 1
If you use SliverChildBuilderDelegate, you should check child widget for key.
example:
List<String> data = ['first', 'second'];
SliverList(
delegate:
SliverChildBuilderDelegate((context, index) {
return ChildWidget(data[index], UniqueKey());
}, childCount: data.length),
);
Keys helps rebuilding children when you update data.
class ChildWidget extends StatelessWidget {
const ChildWidget(this.sourceData, key) : super(key: key);
final String sourceData;
@override
Widget build(BuildContext context) {
return Text(sourceData ?? '');
}
}
If you need more information how keys work, please visit https://api.flutter.dev/flutter/widgets/GlobalKey-class.html or https://www.youtube.com/watch?v=kn0EOS-ZiIc
Solution 2
I hope I'm not too late. And my answer would be useful.
import 'dart:math' as math;
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: SafeArea(
child: MyHomePage(),
),
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Order> orderList = [
Order(status: Status.delivered),
Order(status: Status.canceled),
Order(status: Status.delivered),
];
void _showStatus(int index, Status status) {
setState(() {
currentStatusToShow = status;
});
}
Status currentStatusToShow = Status.canceled;
@override
Widget build(BuildContext context) {
var showList = orderList
.where((order) => order.status == currentStatusToShow)
.toList();
return CustomScrollView(
slivers: [
SliverPadding(
padding: EdgeInsets.only(bottom: 20, top: 50),
sliver: SliverToBoxAdapter(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
RaisedButton(
color: Colors.red,
onPressed: () => _showStatus(0, Status.canceled),
child: Text('Cancel'),
),
RaisedButton(
color: Colors.green,
onPressed: () => _showStatus(0, Status.delivered),
child: Text('delivered'),
)
],
)
],
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => MyOrdersCard(
order: showList[index],
),
childCount: showList.length,
),
),
],
);
}
}
class MyOrdersCard extends StatelessWidget {
const MyOrdersCard({
Key key,
this.order,
this.changeStatus,
}) : super(key: key);
final Order order;
final void Function(Status) changeStatus;
Color get color {
switch (order.status) {
case Status.processing:
return Colors.yellow;
case Status.canceled:
return Colors.red;
case Status.delivered:
default:
return Colors.green;
}
}
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
height: 100,
color: color,
child: Column(
children: [
Text(order.status.toString()),
SizedBox(height: 20),
Text(order.orderId)
],
),
);
}
}
enum Status { processing, canceled, delivered }
class Order {
Order({this.status});
Status status;
String orderId = 'SA-${math.Random().nextInt(1040)}';
}
ANUP SAJJAN
Updated on December 15, 2022Comments
-
ANUP SAJJAN over 1 year
I am having a UI which shows user the delivered Items and cancelled items in a grocery app .
'Delivered' ->pressing this button shows the delivered item card widgets.
and on the same page
'Cancelled' ->pressing this button shows the cancelled item card widgets.
I am using SliverChildBuilderDelegate to build card widgets based on the current input of the user .(Initially when the page is loaded I am showing all delivered Items .)
SliverList( delegate: SliverChildBuilderDelegate( (context, index) => MyOrdersCard( orderNumber: orderList[index][0], trackingNumber: orderList[index][1], quantity: orderList[index][2], totalAmount: orderList[index][3], status: orderList[index][4], date: orderList[index][5], statusColor: orderList[index][6], ), childCount: orderList.length, ), ),
I am filling the list ('orderList' ) in the below code with the status variable having the status of the order .
var orderList = []; handleClick(status) { setState(() { orderList = []; var color; switch (status) { case 'Processing': color = Colors.yellow; activeBtn = 2; break; case 'Cancelled': color = Colors.red; activeBtn = 3; break; case 'Delivered': color = Colors.green; activeBtn = 1; break; } for (int i = 0; i < 10; i++) { orderList.add([ '123457' + '$i', '17abc999', 3, 500, status, '0$i-12-2019', color ]); } });
When the button is pressed the above function is called with the status as the argument .
RaisedButton( child: Text('Delivered'), onPressed: () { handleClick('Delivered'); }, ), RaisedButton( child: Text('Processing'), onPressed: () { handleClick('Processing'); }, ),
Now , the UI displays all delivered card items correctly , but if user presses 'Cancelled' Button then the 'handleClick' function is called and the list is filled with all the new cancelled orders and status is also filled as cancelled. But the sliverChildBuilder is not getting refreshed . The UI remains the same . If I scroll below then I can see the cancelled orders , because they are builded freshly as the scroll down happens .Now if I scroll up again then I can see all cancelled orders because the old builded cards were destroyed and sliverChildBuilder builded the cards as the scroll up happened on the fly . I don't want this. I want the sliverChildBuilder to destroy all old children created for 'delivered' items when the 'cancelled' Button is pressed and render all new cancelled orders from the new list ('orderList'). Is there any way I could do this ? Hope my problem is clear!
-
user1012500 over 3 yearsHi did you resolve this - I'm facing the same issue?
-
ANUP SAJJAN over 3 yearsHi @user1012500, kheral answer does solve it. But if you don't wanna change your code, then you can have a look at this solution, which is by the way is just a work-around. stackoverflow.com/a/61918380/12257758
-
-
ANUP SAJJAN over 3 yearsThanks for the response. But I think you took the question wrong. My question was, if you have 2 buttons on top of the screen called delivered and cancelled and if you press delivered then the below list only shows the delivered orders, if you press cancelled, then it shows only cancelled orders. But this was not happening.
-
ANUP SAJJAN over 3 yearsthankyou! This seems to work correctly. But can you answer, why my code was not working? Because even after doing setState and updating the list with new values, the UI was not refreshing.
-
Kherel over 3 yearsit's hard to answer without seeng full example, if you show a git repository with the project, I probably could answer your question.
-
ANUP SAJJAN over 3 yearsWell, this was an old project of mine, I had already fixed this. I think I have provided a decent amount of code in question. But it's okay if you don't get it. Thanks for the help:)