Reputation: 21
we have a performance problem in our calendar widget. We want to display 3 Years in a scroll view. Each day should be displayed and some days should be marked. The problem is, that the rendering time of this widget is on some devises up to 5 seconds. I think the problem is that we need over 1000 Text widgets to display it. Does someone have an idea how to improve it?
I have written a small sample app. There are many simplifications in it, like every month has 31 days and the layout is bad, but it shows what we want and that it is too slow.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool year = false;
@override
Widget build(BuildContext context) {
const Key currentYearKey = Key('currentYearKey');
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() {
year = !year;
})),
appBar: AppBar(
title: Text(widget.title),
),
body: year
? CustomScrollView(
center: currentYearKey,
slivers: [
SliverToBoxAdapter(
child: Column(
children: [Year(), Year()],
),
),
SliverToBoxAdapter(
child: Year(),
key: currentYearKey,
)
],
)
: Text("1"));
}
}
class Year extends StatelessWidget {
const Year({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Year XX"),
...List.generate(
4,
(rowIndex) => Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: List.generate(
3,
(columnIndex) {
return Expanded(
child: Month(
daySize: 14,
markedDays: [1, 3, 26, 30],
));
},
),
),
),
]);
}
}
class Month extends StatelessWidget {
const Month({required this.markedDays, required this.daySize, Key? key})
: super(key: key);
final List<int> markedDays;
final double daySize;
@override
Widget build(BuildContext context) {
return GridView.count(
padding: EdgeInsets.zero,
crossAxisCount: 7,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: List.generate(31, (index) {
final bool isMarked = markedDays.contains(index);
return Center(
child: Container(
height: daySize,
width: daySize,
decoration:
isMarked ? BoxDecoration(color: Colors.lightBlue) : null,
child: Text(
"$index",
style: isMarked ? TextStyle(fontWeight: FontWeight.bold) : null,
),
),
);
}));
}
}
We tried to make as much as possible const, it improved it abit, and make it around 30% faster, but we need it much faster. We also tried to replace the GridView in the month through a table or row/column construct, but it does not help.
Upvotes: 1
Views: 1308
Reputation: 31
As of right now, with every SliverToBoxAdapter -> Column group, flutter tries to lazily render, but each item in the CustomScrollview
is too large rendering ends up takes a lot of time.
I suggest you use Multisliver from package sliver_tools instead of SliverToBoxAdapter -> Column as you are doing right now...
..essentially try to turn as many "Box" multichild widget into a Sliver
widget, as you can. This would make it such that flutter will now render them lazily.
Column
-> Column
-> ... -> Column
would turn into Multisliver
-> ... (you get the gist).
and finally, to avoid shrinkWrapping your GridView, maybe use Wrap widget instead?
I hope this helps you or someone else who comes across this. :)
edit: for cases where you need to decorate a multichild widget (add background color, shadow, etc), use SliverStack
and wrap your decorating widget with a SliverPositioned.filled
widget, all these are from package sliver_tools. cheers 🥂
Upvotes: 0
Reputation: 21
The solution was to compile it in release mode. It does still need some time to render, but much less. Thanks @rszf for the help.
For those who have the same problem, and don't know the build modes, here is some information: https://docs.flutter.dev/testing/build-modes
Upvotes: 1
Reputation: 3671
In this case you defiantly must use GridView.builder / ListView.builder.
This will lead to build widgets on demand. Instead of building everything at once which is Bad practice.
Upvotes: 0
Reputation: 201
Instead of columns
use ListView.builder
, this helped me with long lists:
https://docs.flutter.dev/cookbook/lists/long-lists
Upvotes: 1